From: Jitin George Date: Wed, 14 Feb 2018 09:45:50 +0000 (+0530) Subject: Resolved Issues X-Git-Tag: v3.1-beta1~286^2~5 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e29294b49a587f0b8e2e9eac9e83863b6ae660cf;p=esp-idf Resolved Issues --- diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp-tls.c index e327921759..b56dd2db37 100644 --- a/components/esp-tls/esp-tls.c +++ b/components/esp-tls/esp-tls.c @@ -1,3 +1,16 @@ +// Copyright 2017-2018 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 #include #include @@ -32,10 +45,10 @@ static struct addrinfo *resolve_host_name(const char *host, size_t hostlen) return NULL; } - ESP_LOGD(TAG, "host:%s: strlen %zu\n", use_host, hostlen); + 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:\n", use_host); + ESP_LOGE(TAG, "couldn't get hostname for :%s:", use_host); free(use_host); return NULL; } @@ -43,18 +56,18 @@ static struct addrinfo *resolve_host_name(const char *host, size_t hostlen) return res; } -static ssize_t tcp_read(struct esp_tls *tls, char *data, size_t datalen) +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(struct esp_tls *tls, char *data, size_t datalen) +static ssize_t tls_read(esp_tls_t *tls, char *data, size_t datalen) { ssize_t ret = SSL_read(tls->ssl, data, datalen); if (ret < 0) { int err = SSL_get_error(tls->ssl, ret); if (err != SSL_ERROR_WANT_WRITE && err != SSL_ERROR_WANT_READ) { - ESP_LOGE(TAG, "read error :%d:\n", ret); + ESP_LOGE(TAG, "read error :%d:", ret); } return -err; } @@ -104,11 +117,12 @@ err_freeaddr: return -1; } -static int create_ssl_handle(struct esp_tls *tls, const char *hostname, size_t hostlen, struct esp_tls_cfg *cfg) +static int create_ssl_handle(esp_tls_t *tls, const char *hostname, size_t hostlen, const esp_tls_cfg_t *cfg) { int ret; - - SSL_CTX *ssl_ctx = SSL_CTX_new(TLSv1_2_client_method()); + + const SSL_METHOD *method = cfg->ssl_method!= NULL ? cfg->ssl_method : TLSv1_2_client_method(); + SSL_CTX *ssl_ctx = SSL_CTX_new(method); if (!ssl_ctx) { return -1; } @@ -128,9 +142,13 @@ static int create_ssl_handle(struct esp_tls *tls, const char *hostname, size_t h X509 *ca = PEM_read_bio_X509(bio, NULL, 0, NULL); if (!ca) { - ESP_LOGE(TAG, "CA Error\n"); + ESP_LOGE(TAG, "CA Error"); + X509_free(ca); + BIO_free(bio); + SSL_CTX_free(ssl_ctx); + return -1; } - ESP_LOGD(TAG, "CA OK\n"); + ESP_LOGD(TAG, "CA OK"); X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), ca); @@ -149,8 +167,8 @@ static int create_ssl_handle(struct esp_tls *tls, const char *hostname, size_t h char *use_host = strndup(hostname, hostlen); if (!use_host) { - SSL_CTX_free(ssl_ctx); - return -1; + SSL_CTX_free(ssl_ctx); + return -1; } SSL_set_tlsext_host_name(ssl, use_host); free(use_host); @@ -169,7 +187,10 @@ static int create_ssl_handle(struct esp_tls *tls, const char *hostname, size_t h return 0; } -void esp_tls_conn_delete(struct esp_tls *tls) +/** + * @brief Close the TLS connection and free any allocated resources. + */ +void esp_tls_conn_delete(esp_tls_t *tls) { if (!tls) { return; @@ -186,32 +207,35 @@ void esp_tls_conn_delete(struct esp_tls *tls) free(tls); }; -static ssize_t tcp_write(struct esp_tls *tls, const char *data, size_t datalen) +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(struct esp_tls *tls, const char *data, size_t datalen) +static ssize_t tls_write(esp_tls_t *tls, const char *data, size_t datalen) { ssize_t ret = SSL_write(tls->ssl, data, datalen); if (ret < 0) { int err = SSL_get_error(tls->ssl, ret); if (err != SSL_ERROR_WANT_WRITE && err != SSL_ERROR_WANT_READ) { - ESP_LOGE(TAG, "write error :%d:\n", ret); + ESP_LOGE(TAG, "write error :%d:", ret); } return -err; } return ret; } -struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, struct esp_tls_cfg *cfg) +/** + * @brief Create a new TLS/SSL connection + */ +esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg) { int sockfd = esp_tcp_connect(hostname, hostlen, port); if (sockfd < 0) { return NULL; } - struct esp_tls *tls = (struct esp_tls *)calloc(1, sizeof(struct esp_tls)); + esp_tls_t *tls = (esp_tls_t *)calloc(1, sizeof(esp_tls_t)); if (!tls) { close(sockfd); return NULL; @@ -228,6 +252,12 @@ struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, st tls->read = tls_read; tls->write = tls_write; } + + if(cfg->non_block == true) { + int flags = fcntl(tls->sockfd, F_GETFL, 0); + fcntl(tls->sockfd, F_SETFL, flags | O_NONBLOCK); + } + return tls; } @@ -245,7 +275,10 @@ static int get_port(const char *url, struct http_parser_url *u) return 0; } -struct esp_tls *esp_tls_conn_http_new(const char *url, struct esp_tls_cfg *cfg) +/** + * @brief Create a new TLS/SSL connection with a given "HTTP" url + */ +esp_tls_t *esp_tls_conn_http_new(const char *url, const esp_tls_cfg_t *cfg) { /* Parse URI */ struct http_parser_url u; diff --git a/components/esp-tls/esp-tls.h b/components/esp-tls/esp-tls.h index 0327acc700..182034278a 100644 --- a/components/esp-tls/esp-tls.h +++ b/components/esp-tls/esp-tls.h @@ -1,58 +1,149 @@ +// Copyright 2017-2018 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_H_ #define _ESP_TLS_H_ #include #include #include +#include #ifdef __cplusplus extern "C" { #endif -struct esp_tls_cfg { - /* 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: - * "\x02h2" - * - where the first '2' is the length of the protocol and - * - the subsequent 'h2' is the protocol name - */ - const unsigned char *alpn_protos; - const unsigned char *cacert_pem_buf; - const unsigned int cacert_pem_bytes; -}; - -struct esp_tls { - SSL_CTX *ctx; - SSL *ssl; - int sockfd; - ssize_t (*read)(struct esp_tls *tls, char *data, size_t datalen); - ssize_t (*write)(struct esp_tls *tls, const char *data, size_t datalen); -}; - -/* +/** + * @brief ESP-TLS configuration parameters + */ +typedef struct esp_tls_cfg { + const unsigned 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: + "\x02h2" + - where the first '2' is the length of the protocol and + - the subsequent 'h2' is the protocol name */ + const unsigned char *cacert_pem_buf; /*!< Certificate Authority's certificate in a buffer */ + const unsigned int cacert_pem_bytes; /*!< Size of Certificate Authority certificate + pointed to by cacert_pem_buf */ + const SSL_METHOD *ssl_method; /*!< SSL method that describes internal ssl library + methods/functions which implements the various protocol + versions. If set to NULL, it defaults to + method returned by TLSv1_2_client_method() API. */ + bool non_block; /*!< Configure non-blocking mode. If set to true the + underneath socket will be configured in non + blocking mode after tls session is established */ +} esp_tls_cfg_t; + +/** + * @brief ESP-TLS Connection Handle + */ +typedef struct esp_tls { + SSL_CTX *ctx; /*!< SSL_CTX object is used to establish + TLS/SSL enabled connection */ + SSL *ssl; /*!< SSL object which is needed to hold the data for a + TLS/SSL connection. The new structure inherits the settings of the + underlying context ctx: connection method (SSLv2/v3/TLSv1), + options, verification settings, timeout settings. */ + 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 + connection. */ + ssize_t (*write)(struct esp_tls *tls, const char *data, size_t datalen); /*!< Callback function for writing data to TLS/SSL + connection. */ +} esp_tls_t; + +/** + * @brief Create a new TLS/SSL connection * - * cfg: If you wish to open non-TLS connection, keep this NULL. For TLS - * connection, a pass pointer to 'struct esp_tls_cfg'. At a minimum, this - * structure should be zero-initialized. + * This function establishes a TLS/SSL connection with the specified host. + * + * @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. */ -struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, struct esp_tls_cfg *cfg); +esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg); -/* Convenience API for HTTP URIs */ -struct esp_tls *esp_tls_conn_http_new(const char *url, struct esp_tls_cfg *cfg); - -static inline ssize_t esp_tls_conn_write(struct esp_tls *tls, const char *data, size_t datalen) +/** + * @brief Create a new TLS/SSL connection with a given "HTTP" url + * + * The behaviour is same as esp_tls_conn_new() API. However this API accepts host's url. + * + * @param[in] url url of 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_http_new(const char *url, const esp_tls_cfg_t *cfg); + +/** + * @brief Write from buffer 'data' into specified tls connection. + * + * @param[in] tls pointer to esp-tls as esp-tls handle. + * @param[in] data Buffer from which data will be written. + * @param[in] datalen Length of data buffer. + * + * @return + * - >0 if write operation was successful, the return value is the number + * of bytes actually written to the TLS/SSL connection. + * - 0 if write operation was not successful. The underlying + * connection was closed. + * - <0 if write operation was not successful, because either an + * error occured or an action must be taken by the calling process. + */ +static inline ssize_t esp_tls_conn_write(esp_tls_t *tls, const void *data, size_t datalen) { - return tls->write(tls, data, datalen); + return tls->write(tls, (char *)data, datalen); } -static inline ssize_t esp_tls_conn_read(struct esp_tls *tls, char *data, size_t datalen) +/** + * @brief Read from specified tls connection into the buffer 'data'. + * + * @param[in] tls pointer to esp-tls as esp-tls handle. + * @param[in] data Buffer to hold read data. + * @param[in] datalen Length of data buffer. + * + * @return +* - >0 if read operation was successful, the return value is the number +* of bytes actually read from the TLS/SSL connection. +* - 0 if read operation was not successful. The underlying +* connection was closed. +* - <0 if read operation was not successful, because either an +* error occured or an action must be taken by the calling process. +*/ +static inline ssize_t esp_tls_conn_read(esp_tls_t *tls, void *data, size_t datalen) { - return tls->read(tls, data, datalen); + return tls->read(tls, (char *)data, datalen); } -void esp_tls_conn_delete(struct esp_tls *tls); +/** + * @brief Close the TLS/SSL connection and free any allocated resources. + * + * This function should be called to close each tls connection opened with esp_tls_conn_new() or + * esp_tls_conn_http_new() APIs. + * + * @param[in] tls pointer to esp-tls as esp-tls handle. + */ +void esp_tls_conn_delete(esp_tls_t *tls); #ifdef __cplusplus } diff --git a/docs/Doxyfile b/docs/Doxyfile index 9e9f377802..e0c1e0d11b 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -87,6 +87,9 @@ INPUT = \ ## ## Protocols - API Reference ## + ## ESP-TLS + ../../components/esp-tls/esp_tls.h \ + ## mDNS ../../components/mdns/include/mdns.h \ ## ## Storage - API Reference diff --git a/docs/api-reference/protocols/esp_tls.rst b/docs/api-reference/protocols/esp_tls.rst new file mode 100644 index 0000000000..1552d7dab2 --- /dev/null +++ b/docs/api-reference/protocols/esp_tls.rst @@ -0,0 +1,24 @@ +ESP-TLS +======= + +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 +Any application layer protocol like HTTP1, HTTP2 etc can be executed on top of this layer. + +Application Example +------------------- + +Simple HTTPS example that uses ESP-TLS to establish a secure socket connection: :example:`protocols/https_request`. + +API Reference +------------- + +.. include:: /_build/inc/esp_tls.inc + diff --git a/docs/en/api-reference/protocols/index.rst b/docs/en/api-reference/protocols/index.rst index b5572d8cd6..fb9ca36a76 100644 --- a/docs/en/api-reference/protocols/index.rst +++ b/docs/en/api-reference/protocols/index.rst @@ -5,6 +5,6 @@ Protocols API :maxdepth: 1 mDNS - + ESP-TLS Example code for this API section is provided in :example:`protocols` directory of ESP-IDF examples. diff --git a/examples/protocols/http2_request/components/sh2lib/sh2lib.c b/examples/protocols/http2_request/components/sh2lib/sh2lib.c index 04817e16fb..750ec45a2c 100644 --- a/examples/protocols/http2_request/components/sh2lib/sh2lib.c +++ b/examples/protocols/http2_request/components/sh2lib/sh2lib.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -75,7 +74,7 @@ static int do_ssl_connect(struct sh2lib_handle *hd, int sockfd, const char *host static ssize_t callback_send_inner(struct sh2lib_handle *hd, const uint8_t *data, size_t length) { - int rv = esp_tls_conn_write(hd->http2_tls, (const char *)data, (int)length); + int rv = esp_tls_conn_write(hd->http2_tls, data, length); if (rv <= 0) { if (rv == -SSL_ERROR_WANT_WRITE || rv == -SSL_ERROR_WANT_READ) { rv = NGHTTP2_ERR_WOULDBLOCK; @@ -279,8 +278,10 @@ static int do_http2_connect(struct sh2lib_handle *hd) int sh2lib_connect(struct sh2lib_handle *hd, const char *uri) { memset(hd, 0, sizeof(*hd)); - struct esp_tls_cfg tls_cfg; - tls_cfg.alpn_protos = (unsigned char *) "\x02h2"; + esp_tls_cfg_t tls_cfg = { + .alpn_protos = (unsigned char *) "\x02h2", + .non_block = true, + }; if ((hd->http2_tls = esp_tls_conn_http_new(uri, &tls_cfg)) == NULL) { ESP_LOGE(TAG, "[sh2-connect] esp-tls connection failed"); goto error; @@ -292,9 +293,6 @@ int sh2lib_connect(struct sh2lib_handle *hd, const char *uri) goto error; } - int flags = fcntl(hd->http2_tls->sockfd, F_GETFL, 0); - fcntl(hd->http2_tls->sockfd, F_SETFL, flags | O_NONBLOCK); - return 0; error: sh2lib_free(hd); diff --git a/examples/protocols/http2_request/components/sh2lib/sh2lib.h b/examples/protocols/http2_request/components/sh2lib/sh2lib.h index 0112768bbb..b69c367394 100644 --- a/examples/protocols/http2_request/components/sh2lib/sh2lib.h +++ b/examples/protocols/http2_request/components/sh2lib/sh2lib.h @@ -14,7 +14,7 @@ #ifndef __ESP_EXAMPLE_SH2_LIB_H_ #define __ESP_EXAMPLE_SH2_LIB_H_ -#include +#include "esp-tls.h" #include /* diff --git a/examples/protocols/https_request/README.md b/examples/protocols/https_request/README.md index fb7c6df95c..7b158b6af7 100644 --- a/examples/protocols/https_request/README.md +++ b/examples/protocols/https_request/README.md @@ -1,5 +1,5 @@ # HTTPS Request Example -Uses an mbedTLS socket to make a very simple HTTPS request over a secure connection, including verifying the server TLS certificate. +Uses APIs from `esp-tls` component to make a very simple HTTPS request over a secure connection, including verifying the server TLS certificate. See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/protocols/https_request/main/https_request_example_main.c b/examples/protocols/https_request/main/https_request_example_main.c index 1faab94325..9dca5b61ac 100644 --- a/examples/protocols/https_request/main/https_request_example_main.c +++ b/examples/protocols/https_request/main/https_request_example_main.c @@ -38,15 +38,6 @@ #include "lwip/netdb.h" #include "lwip/dns.h" -#include "mbedtls/platform.h" -#include "mbedtls/net_sockets.h" -#include "mbedtls/esp_debug.h" -#include "mbedtls/ssl.h" -#include "mbedtls/entropy.h" -#include "mbedtls/ctr_drbg.h" -#include "mbedtls/error.h" -#include "mbedtls/certs.h" - #include "esp-tls.h" /* The examples use simple WiFi configuration that you can set via @@ -144,7 +135,7 @@ static void https_get_task(void *pvParameters) xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); ESP_LOGI(TAG, "Connected to AP"); - struct esp_tls_cfg cfg = { + esp_tls_cfg_t cfg = { .cacert_pem_buf = server_root_cert_pem_start, .cacert_pem_bytes = server_root_cert_pem_end - server_root_cert_pem_start, }; @@ -161,13 +152,13 @@ static void https_get_task(void *pvParameters) size_t written_bytes = 0; do { ret = esp_tls_conn_write(tls, - (const char *)REQUEST + written_bytes, + REQUEST + written_bytes, strlen(REQUEST) - written_bytes); if (ret >= 0) { ESP_LOGI(TAG, "%d bytes written", ret); written_bytes += ret; - } else if (-ret != SSL_ERROR_WANT_WRITE && -ret != SSL_ERROR_WANT_READ) { - ESP_LOGE(TAG, "esp_tls_conn_write returned -0x%x", ret); + } else if (ret != -SSL_ERROR_WANT_WRITE && ret != -SSL_ERROR_WANT_READ) { + ESP_LOGE(TAG, "esp_tls_conn_write returned 0x%x", ret); goto exit; } } while(written_bytes < strlen(REQUEST)); @@ -180,17 +171,12 @@ static void https_get_task(void *pvParameters) bzero(buf, sizeof(buf)); ret = esp_tls_conn_read(tls, (char *)buf, len); - if(-ret == SSL_ERROR_WANT_WRITE || -ret == SSL_ERROR_WANT_READ) + if(ret == -SSL_ERROR_WANT_WRITE || ret == -SSL_ERROR_WANT_READ) continue; - - if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { - ret = 0; - break; - } - + if(ret < 0) { - ESP_LOGE(TAG, "esp_tls_conn_read returned -0x%x", ret); + ESP_LOGE(TAG, "esp_tls_conn_read returned 0x%x", ret); break; } @@ -209,12 +195,6 @@ static void https_get_task(void *pvParameters) } while(1); exit: - if(ret != 0) - { - mbedtls_strerror(ret, buf, 100); - ESP_LOGE(TAG, "Last error was: -0x%x - %s", -ret, buf); - } - esp_tls_conn_delete(tls); putchar('\n'); // JSON output doesn't have a newline at end