feat(esp-tls): update esp-tls from esp-idf

Commit ID: 03d07741
This commit is contained in:
Dong Heng
2019-11-18 17:37:28 +08:00
parent a76dbc7ddd
commit 595b51b15f
10 changed files with 1711 additions and 638 deletions

View File

@ -1,7 +1,21 @@
set(COMPONENT_SRCS "esp_tls.c") set(srcs esp_tls.c)
set(COMPONENT_ADD_INCLUDEDIRS ".") if(CONFIG_ESP_TLS_USING_MBEDTLS)
list(APPEND srcs
"esp_tls_mbedtls.c")
endif()
set(COMPONENT_REQUIRES mbedtls) if(CONFIG_ESP_TLS_USING_WOLFSSL)
set(COMPONENT_PRIV_REQUIRES lwip http_parser) list(APPEND srcs
"esp_tls_wolfssl.c")
endif()
register_component() 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

@ -0,0 +1,51 @@
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, 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
select MBEDTLS_KEY_EXCHANGE_ECDHE_PSK
select MBEDTLS_KEY_EXCHANGE_RSA_PSK
default n
help
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_ADD_INCLUDEDIRS := . 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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -22,18 +22,14 @@
#include <http_parser.h> #include <http_parser.h>
#include "esp_tls.h" #include "esp_tls.h"
#include "esp_tls_error_capture_internal.h"
#include <errno.h> #include <errno.h>
#if LWIP_IPV6
#define ESP_TLS_IPV6
#endif
static const char *TAG = "esp-tls"; static const char *TAG = "esp-tls";
#if CONFIG_SSL_USING_MBEDTLS
static mbedtls_x509_crt *global_cacert = NULL; #ifdef CONFIG_ESP_TLS_USING_MBEDTLS
#elif CONFIG_SSL_USING_WOLFSSL #include "esp_tls_mbedtls.h"
static unsigned char *global_cacert = NULL; #elif CONFIG_ESP_TLS_USING_WOLFSSL
static unsigned int global_cacert_pem_bytes = 0; #include "esp_tls_wolfssl.h"
#endif #endif
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
@ -43,27 +39,43 @@ static unsigned int global_cacert_pem_bytes = 0;
#define ESP_LOGE(TAG, ...) printf(__VA_ARGS__); #define ESP_LOGE(TAG, ...) printf(__VA_ARGS__);
#endif #endif
static struct addrinfo *resolve_host_name(const char *host, size_t hostlen) #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)
{ {
struct addrinfo hints; return _esp_create_ssl_handle(hostname, hostlen, cfg, tls);
memset(&hints, 0, sizeof(hints)); }
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
char *use_host = strndup(host, hostlen); static esp_err_t esp_tls_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg)
if (!use_host) { {
return NULL; return _esp_tls_handshake(tls, cfg);
}
ESP_LOGD(TAG, "host:%s: strlen %lu", use_host, (unsigned long)hostlen);
struct addrinfo *res;
if (getaddrinfo(use_host, NULL, &hints, &res)) {
ESP_LOGE(TAG, "couldn't get hostname for :%s:", use_host);
free(use_host);
return NULL;
}
free(use_host);
return res;
} }
static ssize_t tcp_read(esp_tls_t *tls, char *data, size_t datalen) static ssize_t tcp_read(esp_tls_t *tls, char *data, size_t datalen)
@ -71,413 +83,9 @@ static ssize_t tcp_read(esp_tls_t *tls, char *data, size_t datalen)
return recv(tls->sockfd, data, datalen, 0); return recv(tls->sockfd, data, datalen, 0);
} }
static ssize_t tls_read(esp_tls_t *tls, char *data, size_t datalen) static ssize_t tcp_write(esp_tls_t *tls, const char *data, size_t datalen)
{ {
#if CONFIG_SSL_USING_MBEDTLS return send(tls->sockfd, data, datalen, 0);
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_LOGE(TAG, "read error :%d:", ret);
} else {
ret = (ret == MBEDTLS_ERR_SSL_WANT_READ) ? ESP_TLS_ERROR_WANT_READ : ESP_TLS_ERROR_WANT_WRITE;
}
}
#elif CONFIG_SSL_USING_WOLFSSL
ssize_t ret = wolfSSL_read(tls->ssl, (unsigned char *)data, datalen);
if (ret < 0) {
ret = wolfSSL_get_error(tls->ssl, ret);
/* peer sent close notify */
if (ret == WOLFSSL_ERROR_ZERO_RETURN) {
return 0;
}
if (ret != WOLFSSL_ERROR_WANT_READ && ret != WOLFSSL_ERROR_WANT_WRITE) {
ESP_LOGE(TAG, "read error :%d:", ret);
} else {
ret = (ret == WOLFSSL_ERROR_WANT_READ) ? ESP_TLS_ERROR_WANT_READ : ESP_TLS_ERROR_WANT_WRITE;
}
}
#endif
return ret;
}
static void ms_to_timeval(int timeout_ms, struct timeval *tv)
{
tv->tv_sec = timeout_ms / 1000;
tv->tv_usec = (timeout_ms % 1000) * 1000;
}
static int esp_tcp_connect(const char *host, int hostlen, int port, int *sockfd, const esp_tls_cfg_t *cfg)
{
int ret = -1;
struct addrinfo *res = resolve_host_name(host, hostlen);
if (!res) {
return ret;
}
int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (fd < 0) {
ESP_LOGE(TAG, "Failed to create socket (family %d socktype %d protocol %d)", res->ai_family, res->ai_socktype, res->ai_protocol);
goto err_freeaddr;
}
void *addr_ptr;
if (res->ai_family == AF_INET) {
struct sockaddr_in *p = (struct sockaddr_in *)res->ai_addr;
p->sin_port = htons(port);
addr_ptr = p;
#ifdef ESP_TLS_IPV6
} else if (res->ai_family == AF_INET6) {
struct sockaddr_in6 *p = (struct sockaddr_in6 *)res->ai_addr;
p->sin6_port = htons(port);
p->sin6_family = AF_INET6;
addr_ptr = p;
#endif
} else {
ESP_LOGE(TAG, "Unsupported protocol family %d", res->ai_family);
goto err_freesocket;
}
if (cfg) {
if (cfg->timeout_ms >= 0) {
struct timeval tv;
ms_to_timeval(cfg->timeout_ms, &tv);
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
}
if (cfg->non_block) {
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
}
ret = connect(fd, addr_ptr, res->ai_addrlen);
if (ret < 0 && !(errno == EINPROGRESS && cfg && cfg->non_block)) {
ESP_LOGE(TAG, "Failed to connect to host (errno %d)", errno);
goto err_freesocket;
}
*sockfd = fd;
freeaddrinfo(res);
return 0;
err_freesocket:
close(fd);
err_freeaddr:
freeaddrinfo(res);
return ret;
}
#if CONFIG_SSL_USING_MBEDTLS
esp_err_t esp_tls_init_global_ca_store()
{
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;
}
#endif
esp_err_t esp_tls_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 CONFIG_SSL_USING_MBEDTLS
int ret;
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\n\n", -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_OK;
#elif CONFIG_SSL_USING_WOLFSSL
if (global_cacert != NULL) {
esp_tls_free_global_ca_store(global_cacert);
}
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;
#endif
}
void *esp_tls_get_global_ca_store()
{
return (void*)global_cacert;
}
void esp_tls_free_global_ca_store()
{
if (global_cacert) {
#if CONFIG_SSL_USING_MBEDTLS
mbedtls_x509_crt_free(global_cacert);
global_cacert = NULL;
#elif CONFIG_SSL_USING_WOLFSSL
free(global_cacert);
global_cacert = NULL;
global_cacert_pem_bytes = 0;
#endif
}
}
static void verify_certificate(esp_tls_t *tls)
{
#if CONFIG_SSL_USING_MBEDTLS
int flags;
char buf[100];
if ((flags = mbedtls_ssl_get_verify_result(&tls->ssl)) != 0) {
ESP_LOGI(TAG, "Failed to verify peer certificate!");
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.");
}
#elif CONFIG_SSL_USING_WOLFSSL
int flags;
if ((flags = wolfSSL_get_verify_result(tls->ssl)) != WOLFSSL_SUCCESS) {
ESP_LOGE(TAG, "Failed to verify peer certificate %d!", flags);
} else {
ESP_LOGI(TAG, "Certificate verified.");
}
#endif
}
static void esp_tls_cleanup(esp_tls_t *tls)
{
if (!tls) {
return;
}
#if CONFIG_SSL_USING_MBEDTLS
if (tls->cacert_ptr != global_cacert) {
mbedtls_x509_crt_free(tls->cacert_ptr);
}
tls->cacert_ptr = NULL;
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);
#elif CONFIG_SSL_USING_WOLFSSL
wolfSSL_shutdown(tls->ssl);
wolfSSL_free(tls->ssl);
wolfSSL_CTX_free(tls->ctx);
wolfSSL_Cleanup();
#endif
}
static int create_ssl_handle(esp_tls_t *tls, const char *hostname, size_t hostlen, const esp_tls_cfg_t *cfg)
{
int ret;
#if CONFIG_SSL_USING_MBEDTLS
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 ((ret = mbedtls_ctr_drbg_seed(&tls->ctr_drbg,
mbedtls_entropy_func, &tls->entropy, NULL, 0)) != 0) {
ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret);
goto exit;
}
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) {
goto exit;
}
/* 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);
free(use_host);
goto exit;
}
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 %d", ret);
goto exit;
}
#ifdef CONFIG_MBEDTLS_SSL_ALPN
if (cfg->alpn_protos) {
mbedtls_ssl_conf_alpn_protocols(&tls->conf, cfg->alpn_protos);
}
#endif
if (cfg->use_global_ca_store == true) {
if (global_cacert == NULL) {
ESP_LOGE(TAG, "global_cacert is NULL");
goto exit;
}
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);
} else if (cfg->cacert_pem_buf != NULL) {
tls->cacert_ptr = &tls->cacert;
mbedtls_x509_crt_init(tls->cacert_ptr);
ret = mbedtls_x509_crt_parse(tls->cacert_ptr, cfg->cacert_pem_buf, cfg->cacert_pem_bytes);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
goto exit;
}
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&tls->conf, tls->cacert_ptr, NULL);
} else {
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
}
if (cfg->clientcert_pem_buf != NULL && cfg->clientkey_pem_buf != NULL) {
mbedtls_x509_crt_init(&tls->clientcert);
mbedtls_pk_init(&tls->clientkey);
ret = mbedtls_x509_crt_parse(&tls->clientcert, cfg->clientcert_pem_buf, cfg->clientcert_pem_bytes);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
goto exit;
}
ret = mbedtls_pk_parse_key(&tls->clientkey, cfg->clientkey_pem_buf, cfg->clientkey_pem_bytes,
cfg->clientkey_password, cfg->clientkey_password_len);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_pk_parse_keyfile returned -0x%x\n\n", -ret);
goto exit;
}
ret = mbedtls_ssl_conf_own_cert(&tls->conf, &tls->clientcert, &tls->clientkey);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_ssl_conf_own_cert returned -0x%x\n\n", -ret);
goto exit;
}
} 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;
}
mbedtls_ssl_conf_rng(&tls->conf, mbedtls_ctr_drbg_random, &tls->ctr_drbg);
#ifdef CONFIG_MBEDTLS_DEBUG
mbedtls_esp_enable_debug_log(&tls->conf, 4);
#endif
if ((ret = mbedtls_ssl_setup(&tls->ssl, &tls->conf)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x\n\n", -ret);
goto exit;
}
mbedtls_ssl_set_bio(&tls->ssl, &tls->server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
return 0;
exit:
esp_tls_cleanup(tls);
return -1;
#elif CONFIG_SSL_USING_WOLFSSL
ret = wolfSSL_Init();
if (ret != WOLFSSL_SUCCESS) {
ESP_LOGE(TAG, "Init wolfSSL failed: %d", ret);
goto exit;
}
tls->ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
if (!tls->ctx) {
ESP_LOGE(TAG, "Set wolfSSL ctx failed");
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(tls->ssl, *alpn_list, strlen(*alpn_list), WOLFSSL_ALPN_FAILED_ON_MISMATCH) != WOLFSSL_SUCCESS) {
ESP_LOGE(TAG, "Use wolfSSL ALPN failed");
goto exit;
}
}
}
#endif
if (cfg->use_global_ca_store == true) {
wolfSSL_CTX_load_verify_buffer(tls->ctx, global_cacert, global_cacert_pem_bytes, WOLFSSL_FILETYPE_PEM);
wolfSSL_CTX_set_verify(tls->ctx, SSL_VERIFY_PEER, NULL);
} else if (cfg->cacert_pem_buf != NULL) {
wolfSSL_CTX_load_verify_buffer(tls->ctx, cfg->cacert_pem_buf, cfg->cacert_pem_bytes, WOLFSSL_FILETYPE_PEM);
wolfSSL_CTX_set_verify(tls->ctx, SSL_VERIFY_PEER, NULL);
} else {
wolfSSL_CTX_set_verify(tls->ctx, SSL_VERIFY_NONE, NULL);
}
if (cfg->clientcert_pem_buf != NULL && cfg->clientkey_pem_buf != NULL) {
wolfSSL_CTX_use_certificate_buffer(tls->ctx, cfg->clientcert_pem_buf, cfg->clientcert_pem_bytes, WOLFSSL_FILETYPE_PEM);
wolfSSL_CTX_use_PrivateKey_buffer(tls->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->ssl = wolfSSL_new(tls->ctx);
if (!tls->ssl) {
ESP_LOGE(TAG, "Create wolfSSL failed");
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(tls->ssl, use_host);
free(use_host);
#endif
wolfSSL_set_fd(tls->ssl, tls->sockfd);
return 0;
exit:
esp_tls_cleanup(tls);
return -1;
#endif
} }
/** /**
@ -486,48 +94,123 @@ exit:
void esp_tls_conn_delete(esp_tls_t *tls) void esp_tls_conn_delete(esp_tls_t *tls)
{ {
if (tls != NULL) { if (tls != NULL) {
esp_tls_cleanup(tls); _esp_tls_conn_delete(tls);
#if CONFIG_SSL_USING_MBEDTLS
if (tls->is_tls) {
mbedtls_net_free(&tls->server_fd);
} else
#endif
if (tls->sockfd >= 0) { if (tls->sockfd >= 0) {
close(tls->sockfd); close(tls->sockfd);
} }
free(tls); 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) esp_tls_t *esp_tls_init(void)
{ {
#if CONFIG_SSL_USING_MBEDTLS esp_tls_t *tls = (esp_tls_t *)calloc(1, sizeof(esp_tls_t));
ssize_t ret = mbedtls_ssl_write(&tls->ssl, (unsigned char*) data, datalen); if (!tls) {
if (ret < 0) { return NULL;
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "write error :%d:", ret);
} else {
ret = (ret == MBEDTLS_ERR_SSL_WANT_READ) ? ESP_TLS_ERROR_WANT_READ : ESP_TLS_ERROR_WANT_WRITE;
}
} }
return ret; tls->error_handle = calloc(1, sizeof(esp_tls_last_error_t));
#elif CONFIG_SSL_USING_WOLFSSL if (!tls->error_handle) {
ssize_t ret = wolfSSL_write(tls->ssl, (unsigned char*) data, datalen); free(tls);
if (ret < 0) { return NULL;
ret = wolfSSL_get_error(tls->ssl, ret);
if (ret != WOLFSSL_ERROR_WANT_READ && ret != WOLFSSL_ERROR_WANT_WRITE) {
ESP_LOGE(TAG, "write error :%d:", ret);
} else {
ret = (ret == WOLFSSL_ERROR_WANT_READ) ? ESP_TLS_ERROR_WANT_READ : ESP_TLS_ERROR_WANT_WRITE;
}
} }
return ret; #ifdef CONFIG_ESP_TLS_USING_MBEDTLS
tls->server_fd.fd = tls->sockfd = -1;
#endif #endif
return tls;
}
static esp_err_t resolve_host_name(const char *host, size_t hostlen, struct addrinfo **address_info)
{
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
char *use_host = strndup(host, hostlen);
if (!use_host) {
return ESP_ERR_NO_MEM;
}
ESP_LOGD(TAG, "host:%s: strlen %lu", use_host, (unsigned long)hostlen);
if (getaddrinfo(use_host, NULL, &hints, address_info)) {
ESP_LOGE(TAG, "couldn't get hostname for :%s:", use_host);
free(use_host);
return ESP_ERR_ESP_TLS_CANNOT_RESOLVE_HOSTNAME;
}
free(use_host);
return ESP_OK;
}
static void ms_to_timeval(int timeout_ms, struct timeval *tv)
{
tv->tv_sec = timeout_ms / 1000;
tv->tv_usec = (timeout_ms % 1000) * 1000;
}
static esp_err_t esp_tcp_connect(const char *host, int hostlen, int port, int *sockfd, const esp_tls_t *tls, const esp_tls_cfg_t *cfg)
{
esp_err_t ret;
struct addrinfo *addrinfo;
if ((ret = resolve_host_name(host, hostlen, &addrinfo)) != ESP_OK) {
return ret;
}
int fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
if (fd < 0) {
ESP_LOGE(TAG, "Failed to create socket (family %d socktype %d protocol %d)", addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_SYSTEM, errno);
ret = ESP_ERR_ESP_TLS_CANNOT_CREATE_SOCKET;
goto err_freeaddr;
}
void *addr_ptr;
if (addrinfo->ai_family == AF_INET) {
struct sockaddr_in *p = (struct sockaddr_in *)addrinfo->ai_addr;
p->sin_port = htons(port);
addr_ptr = p;
} else if (addrinfo->ai_family == AF_INET6) {
struct sockaddr_in6 *p = (struct sockaddr_in6 *)addrinfo->ai_addr;
p->sin6_port = htons(port);
p->sin6_family = AF_INET6;
addr_ptr = p;
} else {
ESP_LOGE(TAG, "Unsupported protocol family %d", addrinfo->ai_family);
ret = ESP_ERR_ESP_TLS_UNSUPPORTED_PROTOCOL_FAMILY;
goto err_freesocket;
}
if (cfg) {
if (cfg->timeout_ms >= 0) {
struct timeval tv;
ms_to_timeval(cfg->timeout_ms, &tv);
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
}
if (cfg->non_block) {
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
}
ret = connect(fd, addr_ptr, addrinfo->ai_addrlen);
if (ret < 0 && !(errno == EINPROGRESS && cfg && cfg->non_block)) {
ESP_LOGE(TAG, "Failed to connnect to host (errno %d)", errno);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_SYSTEM, errno);
ret = ESP_ERR_ESP_TLS_FAILED_CONNECT_TO_HOST;
goto err_freesocket;
}
*sockfd = fd;
freeaddrinfo(addrinfo);
return ESP_OK;
err_freesocket:
close(fd);
err_freeaddr:
freeaddrinfo(addrinfo);
return ret;
} }
static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls) static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls)
@ -536,118 +219,83 @@ static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, c
ESP_LOGE(TAG, "empty esp_tls parameter"); ESP_LOGE(TAG, "empty esp_tls parameter");
return -1; return -1;
} }
esp_err_t esp_ret;
/* These states are used to keep a tab on connection progress in case of non-blocking connect, /* 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 */ and in case of blocking connect these cases will get executed one after the other */
switch (tls->conn_state) { switch (tls->conn_state) {
case ESP_TLS_INIT: case ESP_TLS_INIT:
tls->sockfd = -1; tls->sockfd = -1;
if (cfg != NULL) { if (cfg != NULL) {
#if CONFIG_SSL_USING_MBEDTLS #ifdef CONFIG_ESP_TLS_USING_MBEDTLS
mbedtls_net_init(&tls->server_fd); mbedtls_net_init(&tls->server_fd);
#endif #endif
tls->is_tls = true; tls->is_tls = true;
} }
int ret = esp_tcp_connect(hostname, hostlen, port, &tls->sockfd, cfg); if ((esp_ret = esp_tcp_connect(hostname, hostlen, port, &tls->sockfd, tls, cfg)) != ESP_OK) {
if (ret < 0) { ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, esp_ret);
return -1; return -1;
} }
if (!cfg) { if (!cfg) {
tls->_read = tcp_read; tls->read = tcp_read;
tls->_write = tcp_write; tls->write = tcp_write;
ESP_LOGD(TAG, "non-tls connection established"); ESP_LOGD(TAG, "non-tls connection established");
return 1; return 1;
} }
if (cfg->non_block) { if (cfg->non_block) {
FD_ZERO(&tls->rset); FD_ZERO(&tls->rset);
FD_SET(tls->sockfd, &tls->rset); FD_SET(tls->sockfd, &tls->rset);
tls->wset = tls->rset; tls->wset = tls->rset;
} }
tls->conn_state = ESP_TLS_CONNECTING; tls->conn_state = ESP_TLS_CONNECTING;
/* falls through */ /* falls through */
case ESP_TLS_CONNECTING: case ESP_TLS_CONNECTING:
if (cfg->non_block) { if (cfg->non_block) {
ESP_LOGD(TAG, "connecting..."); ESP_LOGD(TAG, "connecting...");
struct timeval tv; struct timeval tv;
ms_to_timeval(cfg->timeout_ms, &tv); ms_to_timeval(cfg->timeout_ms, &tv);
/* In case of non-blocking I/O, we use the select() API to check whether /* In case of non-blocking I/O, we use the select() API to check whether
connection has been estbalished or not*/ connection has been estbalished or not*/
if (select(tls->sockfd + 1, &tls->rset, &tls->wset, NULL, if (select(tls->sockfd + 1, &tls->rset, &tls->wset, NULL,
cfg->timeout_ms ? &tv : NULL) == 0) { cfg->timeout_ms ? &tv : NULL) == 0) {
ESP_LOGD(TAG, "select() timed out"); ESP_LOGD(TAG, "select() timed out");
return 0; 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");
tls->conn_state = ESP_TLS_FAIL;
return -1;
}
}
} }
/* By now, the connection has been established */ if (FD_ISSET(tls->sockfd, &tls->rset) || FD_ISSET(tls->sockfd, &tls->wset)) {
ret = create_ssl_handle(tls, hostname, hostlen, cfg); int error;
if (ret != 0) { unsigned int len = sizeof(error);
ESP_LOGD(TAG, "create_ssl_handshake failed"); /* pending error check */
tls->conn_state = ESP_TLS_FAIL; if (getsockopt(tls->sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
return -1; ESP_LOGD(TAG, "Non blocking connect failed");
} ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_SYSTEM, errno);
tls->_read = tls_read; ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED);
tls->_write = tls_write;
tls->conn_state = ESP_TLS_HANDSHAKE;
/* falls through */
case ESP_TLS_HANDSHAKE:
ESP_LOGD(TAG, "handshake in progress...");
#if CONFIG_SSL_USING_MBEDTLS
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);
if (cfg->cacert_pem_buf != NULL || cfg->use_global_ca_store == true) {
/* This is to check whether handshake failed due to invalid certificate*/
verify_certificate(tls);
}
tls->conn_state = ESP_TLS_FAIL; tls->conn_state = ESP_TLS_FAIL;
return -1; 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;
} }
#elif CONFIG_SSL_USING_WOLFSSL }
ret = wolfSSL_connect(tls->ssl); /* By now, the connection has been established */
if (ret == WOLFSSL_SUCCESS) { esp_ret = create_ssl_handle(hostname, hostlen, cfg, tls);
tls->conn_state = ESP_TLS_DONE; if (esp_ret != ESP_OK) {
return 1; ESP_LOGE(TAG, "create_ssl_handle failed");
} else { ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, esp_ret);
int err = wolfSSL_get_error(tls->ssl, ret); tls->conn_state = ESP_TLS_FAIL;
if (err != WOLFSSL_ERROR_WANT_READ && err != WOLFSSL_ERROR_WANT_WRITE) { return -1;
ESP_LOGE(TAG, "wolfSSL_connect returned -0x%x", -ret); }
if (cfg->cacert_pem_buf != NULL || cfg->use_global_ca_store == true) { tls->read = _esp_tls_read;
/* This is to check whether handshake failed due to invalid certificate*/ tls->write = _esp_tls_write;
verify_certificate(tls); tls->conn_state = ESP_TLS_HANDSHAKE;
} /* falls through */
tls->conn_state = ESP_TLS_FAIL; case ESP_TLS_HANDSHAKE:
return -1; ESP_LOGD(TAG, "handshake in progress...");
} return esp_tls_handshake(tls, cfg);
/* Irrespective of blocking or non-blocking I/O, we return on getting wolfSSL_want_read break;
or wolfSSL_want_write during handshake */ case ESP_TLS_FAIL:
return 0; ESP_LOGE(TAG, "failed to open a new connection");;
} break;
#endif default:
break; ESP_LOGE(TAG, "invalid esp-tls state");
case ESP_TLS_FAIL: break;
ESP_LOGE(TAG, "failed to open a new connection");;
break;
default:
ESP_LOGE(TAG, "invalid esp-tls state");
break;
} }
return -1; return -1;
} }
@ -676,10 +324,26 @@ esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const e
return NULL; return NULL;
} }
int esp_tls_conn_new_sync(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls)
{
/* esp_tls_conn_new_sync() is a sync alternative to esp_tls_conn_new_async() with symetric function prototype
it is an alternative to esp_tls_conn_new() which is left for compatibility reasons */
while (1) {
int ret = esp_tls_low_level_conn(hostname, hostlen, port, cfg, tls);
if (ret == 1) {
return ret;
} else if (ret == -1) {
ESP_LOGE(TAG, "Failed to open new connection");
return -1;
}
}
return 0;
}
/* /*
* @brief Create a new TLS/SSL non-blocking connection * @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); return esp_tls_low_level_conn(hostname, hostlen, port, cfg, tls);
} }
@ -707,23 +371,16 @@ esp_tls_t *esp_tls_conn_http_new(const char *url, const esp_tls_cfg_t *cfg)
struct http_parser_url u; struct http_parser_url u;
http_parser_url_init(&u); http_parser_url_init(&u);
http_parser_parse_url(url, strlen(url), 0, &u); http_parser_parse_url(url, strlen(url), 0, &u);
esp_tls_t *tls = esp_tls_init();
/* Connect to host */
return esp_tls_conn_new(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len,
get_port(url, &u), cfg);
}
size_t esp_tls_get_bytes_avail(esp_tls_t *tls)
{
if (!tls) { if (!tls) {
ESP_LOGE(TAG, "empty arg passed to esp_tls_get_bytes_avail()"); return NULL;
return ESP_FAIL;
} }
#if CONFIG_SSL_USING_MBEDTLS /* Connect to host */
return mbedtls_ssl_get_bytes_avail(&tls->ssl); if (esp_tls_conn_new_sync(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len,
#elif CONFIG_SSL_USING_WOLFSSL get_port(url, &u), cfg, tls) == 1) {
return wolfSSL_pending(tls->ssl); return tls;
#endif }
return NULL;
} }
/** /**
@ -738,5 +395,66 @@ int esp_tls_conn_http_new_async(const char *url, const esp_tls_cfg_t *cfg, esp_t
/* Connect to host */ /* Connect to host */
return esp_tls_conn_new_async(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len, 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 a server side TLS/SSL connection
*/
int esp_tls_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls)
{
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)
{
return _esp_tls_server_session_delete(tls);
}
#endif /* CONFIG_ESP_TLS_SERVER */
#endif /* CONFIG_ESP_TLS_USING_MBEDTLS */
ssize_t esp_tls_get_bytes_avail(esp_tls_t *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 *esp_tls_code, int *esp_tls_flags)
{
if (!h) {
return ESP_ERR_INVALID_STATE;
}
esp_err_t last_err = h->last_error;
if (esp_tls_code) {
*esp_tls_code = h->esp_tls_error_code;
}
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

@ -17,8 +17,8 @@
#include <stdbool.h> #include <stdbool.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <fcntl.h> #include <fcntl.h>
#include "esp_err.h"
#if CONFIG_SSL_USING_MBEDTLS #ifdef CONFIG_ESP_TLS_USING_MBEDTLS
#include "mbedtls/platform.h" #include "mbedtls/platform.h"
#include "mbedtls/net_sockets.h" #include "mbedtls/net_sockets.h"
#include "mbedtls/esp_debug.h" #include "mbedtls/esp_debug.h"
@ -27,7 +27,8 @@
#include "mbedtls/ctr_drbg.h" #include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h" #include "mbedtls/error.h"
#include "mbedtls/certs.h" #include "mbedtls/certs.h"
#else #elif CONFIG_ESP_TLS_USING_WOLFSSL
#include "wolfssl/wolfcrypt/settings.h"
#include "wolfssl/ssl.h" #include "wolfssl/ssl.h"
#endif #endif
@ -35,10 +36,44 @@
extern "C" { extern "C" {
#endif #endif
typedef enum { #define ESP_ERR_ESP_TLS_BASE 0x8000 /*!< Starting number of ESP-TLS error codes */
ESP_TLS_ERROR_WANT_READ = -2, #define ESP_ERR_ESP_TLS_CANNOT_RESOLVE_HOSTNAME (ESP_ERR_ESP_TLS_BASE + 0x01) /*!< Error if hostname couldn't be resolved upon tls connection */
ESP_TLS_ERROR_WANT_WRITE = -3, #define ESP_ERR_ESP_TLS_CANNOT_CREATE_SOCKET (ESP_ERR_ESP_TLS_BASE + 0x02) /*!< Failed to create socket */
} esp_tls_error_t; #define ESP_ERR_ESP_TLS_UNSUPPORTED_PROTOCOL_FAMILY (ESP_ERR_ESP_TLS_BASE + 0x03) /*!< Unsupported protocol family */
#define ESP_ERR_ESP_TLS_FAILED_CONNECT_TO_HOST (ESP_ERR_ESP_TLS_BASE + 0x04) /*!< Failed to connect to host */
#define ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED (ESP_ERR_ESP_TLS_BASE + 0x05) /*!< failed to set socket option */
#define ESP_ERR_MBEDTLS_CERT_PARTLY_OK (ESP_ERR_ESP_TLS_BASE + 0x06) /*!< mbedtls parse certificates was partly successful */
#define ESP_ERR_MBEDTLS_CTR_DRBG_SEED_FAILED (ESP_ERR_ESP_TLS_BASE + 0x07) /*!< mbedtls api returned error */
#define ESP_ERR_MBEDTLS_SSL_SET_HOSTNAME_FAILED (ESP_ERR_ESP_TLS_BASE + 0x08) /*!< mbedtls api returned error */
#define ESP_ERR_MBEDTLS_SSL_CONFIG_DEFAULTS_FAILED (ESP_ERR_ESP_TLS_BASE + 0x09) /*!< mbedtls api returned error */
#define ESP_ERR_MBEDTLS_SSL_CONF_ALPN_PROTOCOLS_FAILED (ESP_ERR_ESP_TLS_BASE + 0x0A) /*!< mbedtls api returned error */
#define ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED (ESP_ERR_ESP_TLS_BASE + 0x0B) /*!< mbedtls api returned error */
#define ESP_ERR_MBEDTLS_SSL_CONF_OWN_CERT_FAILED (ESP_ERR_ESP_TLS_BASE + 0x0C) /*!< mbedtls api returned error */
#define ESP_ERR_MBEDTLS_SSL_SETUP_FAILED (ESP_ERR_ESP_TLS_BASE + 0x0D) /*!< mbedtls api returned error */
#define ESP_ERR_MBEDTLS_SSL_WRITE_FAILED (ESP_ERR_ESP_TLS_BASE + 0x0E) /*!< mbedtls api returned error */
#define ESP_ERR_MBEDTLS_PK_PARSE_KEY_FAILED (ESP_ERR_ESP_TLS_BASE + 0x0F) /*!< mbedtls api returned failed */
#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;
/**
* @brief Error structure containing relevant errors in case tls error occurred
*/
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 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;
/** /**
* @brief ESP-TLS Connection State * @brief ESP-TLS Connection State
@ -51,8 +86,32 @@ typedef enum esp_tls_conn_state {
ESP_TLS_DONE, ESP_TLS_DONE,
} esp_tls_conn_state_t; } esp_tls_conn_state_t;
typedef enum esp_tls_role {
ESP_TLS_CLIENT = 0,
ESP_TLS_SERVER,
} esp_tls_role_t;
/**
* @brief ESP-TLS preshared key and hint structure
*/
typedef struct psk_key_hint {
const uint8_t* key; /*!< key in PSK authentication mode in binary format */
const size_t key_size; /*!< length of the key */
const char* hint; /*!< hint in PSK authentication mode in string format */
} psk_hint_key_t;
/** /**
* @brief ESP-TLS configuration parameters * @brief ESP-TLS configuration parameters
*
* @note Note about format of certificates:
* - This structure includes certificates of a Certificate Authority, of client or server as well
* as private keys, which may be of PEM or DER format. In case of PEM format, the buffer must be
* NULL terminated (with NULL character included in certificate size).
* - Certificate Authority's certificate may be a chain of certificates in case of PEM format,
* but could be only one certificate in case of DER format
* - Variables names of certificates and private key buffers and sizes are defined as unions providing
* backward compatibility for legacy *_pem_buf and *_pem_bytes names which suggested only PEM format
* was supported. It is encouraged to use generic names such as cacert_buf and cacert_bytes.
*/ */
typedef struct esp_tls_cfg { typedef struct esp_tls_cfg {
const char **alpn_protos; /*!< Application protocols required for HTTP2. const char **alpn_protos; /*!< Application protocols required for HTTP2.
@ -61,27 +120,50 @@ typedef struct esp_tls_cfg {
The format is length followed by protocol The format is length followed by protocol
name. name.
For the most common cases the following is ok: For the most common cases the following is ok:
"\x02h2" const char **alpn_protos = { "h2", NULL };
- where the first '2' is the length of the protocol and - where 'h2' is the protocol name */
- the subsequent 'h2' is the protocol name */
const unsigned char *cacert_pem_buf; /*!< Certificate Authority's certificate in a buffer. union {
This buffer should be NULL terminated */ const unsigned char *cacert_buf; /*!< Certificate Authority's certificate in a buffer.
Format may be PEM or DER, depending on mbedtls-support
This buffer should be NULL terminated in case of PEM */
const unsigned char *cacert_pem_buf; /*!< CA certificate buffer legacy name */
};
unsigned int cacert_pem_bytes; /*!< Size of Certificate Authority certificate union {
pointed to by cacert_pem_buf */ unsigned int cacert_bytes; /*!< Size of Certificate Authority certificate
pointed to by cacert_buf
(including NULL-terminator in case of PEM format) */
unsigned int cacert_pem_bytes; /*!< Size of Certificate Authority certificate legacy name */
};
const unsigned char *clientcert_pem_buf;/*!< Client certificate in a buffer union {
This buffer should be NULL terminated */ const unsigned char *clientcert_buf; /*!< Client certificate in a buffer
Format may be PEM or DER, depending on mbedtls-support
This buffer should be NULL terminated in case of PEM */
const unsigned char *clientcert_pem_buf; /*!< Client certificate legacy name */
};
unsigned int clientcert_pem_bytes; /*!< Size of client certificate pointed to by union {
clientcert_pem_buf */ unsigned int clientcert_bytes; /*!< Size of client certificate pointed to by
clientcert_pem_buf
(including NULL-terminator in case of PEM format) */
unsigned int clientcert_pem_bytes; /*!< Size of client certificate legacy name */
};
const unsigned char *clientkey_pem_buf; /*!< Client key in a buffer union {
This buffer should be NULL terminated */ const unsigned char *clientkey_buf; /*!< Client key in a buffer
Format may be PEM or DER, depending on mbedtls-support
This buffer should be NULL terminated in case of PEM */
const unsigned char *clientkey_pem_buf; /*!< Client key legacy name */
};
unsigned int clientkey_pem_bytes; /*!< Size of client key pointed to by union {
clientkey_pem_buf */ unsigned int clientkey_bytes; /*!< Size of client key pointed to by
clientkey_pem_buf
(including NULL-terminator in case of PEM format) */
unsigned int clientkey_pem_bytes; /*!< Size of client key legacy name */
};
const unsigned char *clientkey_password;/*!< Client key decryption password string */ const unsigned char *clientkey_password;/*!< Client key decryption password string */
@ -101,13 +183,73 @@ typedef struct esp_tls_cfg {
If NULL, server certificate CN must match hostname. */ If NULL, server certificate CN must match hostname. */
bool skip_common_name; /*!< Skip any validation of server certificate CN field */ bool skip_common_name; /*!< Skip any validation of server certificate CN field */
const psk_hint_key_t* psk_hint_key; /*!< Pointer to PSK hint and key. if not NULL (and certificates are NULL)
then PSK authentication is enabled with configured setup.
Important note: the pointer must be valid for connection */
} esp_tls_cfg_t; } esp_tls_cfg_t;
#ifdef CONFIG_ESP_TLS_SERVER
typedef struct esp_tls_cfg_server {
const char **alpn_protos; /*!< Application protocols required for HTTP2.
If HTTP2/ALPN support is required, a list
of protocols that should be negotiated.
The format is length followed by protocol
name.
For the most common cases the following is ok:
const char **alpn_protos = { "h2", NULL };
- where 'h2' is the protocol name */
union {
const unsigned char *cacert_buf; /*!< Client CA certificate in a buffer.
This buffer should be NULL terminated */
const unsigned char *cacert_pem_buf; /*!< Client CA certificate legacy name */
};
union {
unsigned int cacert_bytes; /*!< Size of client CA certificate
pointed to by cacert_pem_buf */
unsigned int cacert_pem_bytes; /*!< Size of client CA certificate legacy name */
};
union {
const unsigned char *servercert_buf; /*!< Server certificate in a buffer
This buffer should be NULL terminated */
const unsigned char *servercert_pem_buf; /*!< Server certificate legacy name */
};
union {
unsigned int servercert_bytes; /*!< Size of server certificate pointed to by
servercert_pem_buf */
unsigned int servercert_pem_bytes; /*!< Size of server certificate legacy name */
};
union {
const unsigned char *serverkey_buf; /*!< Server key in a buffer
This buffer should be NULL terminated */
const unsigned char *serverkey_pem_buf; /*!< Server key legacy name */
};
union {
unsigned int serverkey_bytes; /*!< Size of server key pointed to by
serverkey_pem_buf */
unsigned int serverkey_pem_bytes; /*!< Size of server key legacy name */
};
const unsigned char *serverkey_password; /*!< Server key decryption password string */
unsigned int serverkey_password_len; /*!< String length of the password pointed to by
serverkey_password */
} esp_tls_cfg_server_t;
#endif /* ! CONFIG_ESP_TLS_SERVER */
/** /**
* @brief ESP-TLS Connection Handle * @brief ESP-TLS Connection Handle
*/ */
typedef struct esp_tls { typedef struct esp_tls {
#if CONFIG_SSL_USING_MBEDTLS #ifdef CONFIG_ESP_TLS_USING_MBEDTLS
mbedtls_ssl_context ssl; /*!< TLS/SSL context */ mbedtls_ssl_context ssl; /*!< TLS/SSL context */
mbedtls_entropy_context entropy; /*!< mbedTLS entropy context structure */ mbedtls_entropy_context entropy; /*!< mbedTLS entropy context structure */
@ -130,16 +272,22 @@ typedef struct esp_tls {
mbedtls_pk_context clientkey; /*!< Container for the private key of the client mbedtls_pk_context clientkey; /*!< Container for the private key of the client
certificate */ certificate */
#else #ifdef CONFIG_ESP_TLS_SERVER
WOLFSSL_CTX *ctx; mbedtls_x509_crt servercert; /*!< Container for the X.509 server certificate */
WOLFSSL *ssl;
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 #endif
int sockfd; /*!< Underlying socket file descriptor. */ int sockfd; /*!< Underlying socket file descriptor. */
ssize_t (*_read)(struct esp_tls *tls, char *data, size_t datalen); /*!< Callback function for reading data from TLS/SSL ssize_t (*read)(struct esp_tls *tls, char *data, size_t datalen); /*!< Callback function for reading data from TLS/SSL
connection. */ connection. */
ssize_t (*_write)(struct esp_tls *tls, const char *data, size_t datalen); /*!< Callback function for writing data to TLS/SSL ssize_t (*write)(struct esp_tls *tls, const char *data, size_t datalen); /*!< Callback function for writing data to TLS/SSL
connection. */ connection. */
esp_tls_conn_state_t conn_state; /*!< ESP-TLS Connection state */ esp_tls_conn_state_t conn_state; /*!< ESP-TLS Connection state */
@ -149,8 +297,50 @@ typedef struct esp_tls {
fd_set wset; /*!< write file descriptors */ fd_set wset; /*!< write file descriptors */
bool is_tls; /*!< indicates connection type (TLS or NON-TLS) */ bool is_tls; /*!< indicates connection type (TLS or NON-TLS) */
esp_tls_role_t role; /*!< esp-tls role
- ESP_TLS_CLIENT
- ESP_TLS_SERVER */
esp_tls_error_handle_t error_handle; /*!< handle to error descriptor */
} esp_tls_t; } esp_tls_t;
/**
* @brief Create TLS connection
*
* This function allocates and initializes esp-tls structure handle.
*
* @return tls Pointer to esp-tls as esp-tls handle if successfully initialized,
* NULL if allocation error
*/
esp_tls_t *esp_tls_init(void);
/**
* @brief Create a new blocking TLS/SSL connection
*
* This function establishes a TLS/SSL connection with the specified host in blocking manner.
*
* Note: This API is present for backward compatibility reasons. Alternative function
* with the same functionality is `esp_tls_conn_new_sync` (and its asynchronous version
* `esp_tls_conn_new_async`)
*
* @param[in] hostname Hostname of the host.
* @param[in] hostlen Length of hostname.
* @param[in] port Port number of the host.
* @param[in] cfg TLS configuration as esp_tls_cfg_t. If you wish to open
* non-TLS connection, keep this NULL. For TLS connection,
* a pass pointer to esp_tls_cfg_t. At a minimum, this
* structure should be zero-initialized.
*
* @return pointer to esp_tls_t, or NULL if connection couldn't be opened.
*/
esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg) __attribute__ ((deprecated));
/** /**
* @brief Create a new blocking TLS/SSL connection * @brief Create a new blocking TLS/SSL connection
* *
@ -163,9 +353,14 @@ typedef struct esp_tls {
* non-TLS connection, keep this NULL. For TLS connection, * non-TLS connection, keep this NULL. For TLS connection,
* a pass pointer to esp_tls_cfg_t. At a minimum, this * a pass pointer to esp_tls_cfg_t. At a minimum, this
* structure should be zero-initialized. * structure should be zero-initialized.
* @return pointer to esp_tls_t, or NULL if connection couldn't be opened. * @param[in] tls Pointer to esp-tls as esp-tls handle.
*
* @return
* - -1 If connection establishment fails.
* - 1 If connection establishment is successful.
* - 0 Reserved for connection state is in progress.
*/ */
esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg); int esp_tls_conn_new_sync(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls);
/** /**
* @brief Create a new blocking TLS/SSL connection with a given "HTTP" url * @brief Create a new blocking TLS/SSL connection with a given "HTTP" url
@ -234,7 +429,7 @@ int esp_tls_conn_http_new_async(const char *url, const esp_tls_cfg_t *cfg, esp_t
*/ */
static inline ssize_t esp_tls_conn_write(esp_tls_t *tls, const void *data, size_t datalen) static inline ssize_t esp_tls_conn_write(esp_tls_t *tls, const void *data, size_t datalen)
{ {
return tls->_write(tls, (char *)data, datalen); return tls->write(tls, (char *)data, datalen);
} }
/** /**
@ -254,7 +449,7 @@ static inline ssize_t esp_tls_conn_write(esp_tls_t *tls, const void *data, size_
*/ */
static inline ssize_t esp_tls_conn_read(esp_tls_t *tls, void *data, size_t datalen) static inline ssize_t esp_tls_conn_read(esp_tls_t *tls, void *data, size_t datalen)
{ {
return tls->_read(tls, (char *)data, datalen); return tls->read(tls, (char *)data, datalen);
} }
/** /**
@ -280,7 +475,7 @@ void esp_tls_conn_delete(esp_tls_t *tls);
* - bytes available in the application data * - bytes available in the application data
* record read buffer * record read buffer
*/ */
size_t esp_tls_get_bytes_avail(esp_tls_t *tls); ssize_t esp_tls_get_bytes_avail(esp_tls_t *tls);
/** /**
* @brief Create a global CA store, initially empty. * @brief Create a global CA store, initially empty.
@ -293,7 +488,7 @@ size_t esp_tls_get_bytes_avail(esp_tls_t *tls);
* - ESP_OK if creating global CA store was successful. * - ESP_OK if creating global CA store was successful.
* - ESP_ERR_NO_MEM if an error occured when allocating the mbedTLS resources. * - ESP_ERR_NO_MEM if an error occured when allocating the mbedTLS resources.
*/ */
esp_err_t esp_tls_init_global_ca_store(); esp_err_t esp_tls_init_global_ca_store(void);
/** /**
* @brief Set the global CA store with the buffer provided in pem format. * @brief Set the global CA store with the buffer provided in pem format.
@ -314,6 +509,32 @@ esp_err_t 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); esp_err_t esp_tls_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes);
/**
* @brief Free the global CA store currently being used.
*
* The memory being used by the global CA store to store all the parsed certificates is
* freed up. The application can call this API if it no longer needs the global CA store.
*/
void esp_tls_free_global_ca_store(void);
/**
* @brief Returns last error in esp_tls with detailed mbedtls related error codes.
* The error information is cleared internally upon return
*
* @param[in] h esp-tls error handle.
* @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 *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. * @brief Get the pointer to the global CA store currently being used.
* *
@ -326,16 +547,36 @@ esp_err_t esp_tls_set_global_ca_store(const unsigned char *cacert_pem_buf, const
* - Pointer to the global CA store currently being used if successful. * - Pointer to the global CA store currently being used if successful.
* - NULL if there is no global CA store set. * - NULL if there is no global CA store set.
*/ */
void *esp_tls_get_global_ca_store(); mbedtls_x509_crt *esp_tls_get_global_ca_store(void);
#ifdef CONFIG_ESP_TLS_SERVER
/**
* @brief Create TLS/SSL server session
*
* This function creates a TLS/SSL server context for already accepted client connection
* and performs TLS/SSL handshake with the client
*
* @param[in] cfg Pointer to esp_tls_cfg_server_t
* @param[in] sockfd FD of accepted connection
* @param[out] tls Pointer to allocated esp_tls_t
*
* @return
* - 0 if successful
* - <0 in case of error
*
*/
int esp_tls_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls);
/** /**
* @brief Free the global CA store currently being used. * @brief Close the server side TLS/SSL connection and free any allocated resources.
* *
* The memory being used by the global CA store to store all the parsed certificates is * This function should be called to close each tls connection opened with esp_tls_server_session_create()
* freed up. The application can call this API if it no longer needs the global CA store. *
* @param[in] tls pointer to esp_tls_t
*/ */
void esp_tls_free_global_ca_store(); void esp_tls_server_session_delete(esp_tls_t *tls);
#endif /* ! CONFIG_ESP_TLS_SERVER */
#endif /* CONFIG_ESP_TLS_USING_MBEDTLS */
#ifdef __cplusplus #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

@ -0,0 +1,69 @@
// Copyright 2017-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.
#ifndef __ESP_TLS_ERROR_CAPTURE_INTERNAL_H__
#define __ESP_TLS_ERROR_CAPTURE_INTERNAL_H__
/**
* Note: this is an implementation placeholder for error logger.
* This version is internal to esp-tls component and only saves single esp_err of last occurred error
*/
#ifdef __cplusplus
extern "C" {
#endif
/**
* Definition of different types/sources of error codes reported
* from different components
*/
typedef enum {
ERR_TYPE_UNKNOWN = 0,
ERR_TYPE_SYSTEM,
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_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)
static inline void esp_int_event_tracker_capture(esp_tls_error_handle_t h, uint32_t type, int code)
{
if (h) {
if (type == ERR_TYPE_ESP) {
h->last_error = code;
} else if (type == ERR_TYPE_MBEDTLS) {
h->esp_tls_error_code = code;
} else if (type == ERR_TYPE_MBEDTLS_CERT_FLAGS) {
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
#endif //__ESP_TLS_ERROR_CAPTURE_INTERNAL_H__

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);