Jelajahi Sumber

Merge branch 'feature/esp-tls' into 'master'

feature/add simplified TLS socket APIs

See merge request idf/esp-idf!1951
Ivan Grokhotkov 7 tahun lalu
induk
melakukan
b11209b070

+ 3 - 0
components/esp-tls/component.mk

@@ -0,0 +1,3 @@
+COMPONENT_SRCDIRS := .
+
+COMPONENT_ADD_INCLUDEDIRS := .

+ 319 - 0
components/esp-tls/esp_tls.c

@@ -0,0 +1,319 @@
+// 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 <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.h"
+
+static const char *TAG = "esp-tls";
+
+#ifdef ESP_PLATFORM
+#include <esp_log.h>
+#else
+#define ESP_LOGD(TAG, ...) //printf(__VA_ARGS__);
+#define ESP_LOGE(TAG, ...) printf(__VA_ARGS__);
+#endif
+
+static struct addrinfo *resolve_host_name(const char *host, size_t hostlen)
+{
+    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 NULL;
+    }
+
+    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)
+{
+    return recv(tls->sockfd, data, datalen, 0);
+}
+
+static ssize_t tls_read(esp_tls_t *tls, char *data, size_t datalen)
+{
+    ssize_t ret = mbedtls_ssl_read(&tls->ssl, (unsigned char *)data, datalen);   
+    if (ret < 0) {
+        if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
+            return 0;
+        }
+        if (ret != MBEDTLS_ERR_SSL_WANT_READ  && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+            ESP_LOGE(TAG, "read error :%d:", ret);
+        }
+    }
+    return ret;
+}
+
+static int esp_tcp_connect(const char *host, int hostlen, int port)
+{
+    struct addrinfo *res = resolve_host_name(host, hostlen);
+    if (!res) {
+        return -1;
+    }
+
+    int ret = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+    if (ret < 0) {
+        goto err_freeaddr;
+    }
+    int fd = ret;
+
+    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;
+    } 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;
+    } else {
+	/* Unsupported Protocol Family */
+        goto err_freesocket;
+    }
+
+    ret = connect(fd, addr_ptr, res->ai_addrlen);
+    if (ret < 0) {
+        goto err_freesocket;
+    }
+
+    freeaddrinfo(res);
+    return fd;
+
+err_freesocket:
+    close(fd);
+err_freeaddr:
+    freeaddrinfo(res);
+    return -1;
+}
+
+static void verify_certificate(esp_tls_t *tls)
+{
+    int flags;
+    char buf[100]; 
+    if ((flags = mbedtls_ssl_get_verify_result(&tls->ssl)) != 0) {
+        ESP_LOGI(TAG, "Failed to verify peer certificate!");
+        bzero(buf, sizeof(buf));
+        mbedtls_x509_crt_verify_info(buf, sizeof(buf), "  ! ", flags);
+        ESP_LOGI(TAG, "verification info: %s", buf);
+    } else {
+        ESP_LOGI(TAG, "Certificate verified.");
+    }
+}
+
+static void mbedtls_cleanup(esp_tls_t *tls) 
+{
+    if (!tls) {
+        return;
+    }
+    
+    mbedtls_entropy_free(&tls->entropy);
+    mbedtls_ssl_config_free(&tls->conf);
+    mbedtls_ctr_drbg_free(&tls->ctr_drbg);
+    mbedtls_ssl_free(&tls->ssl);
+    mbedtls_net_free(&tls->server_fd);
+}
+
+static int create_ssl_handle(esp_tls_t *tls, const char *hostname, size_t hostlen, const esp_tls_cfg_t *cfg)
+{
+    int ret;
+    
+    mbedtls_net_init(&tls->server_fd);
+    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;        
+    }
+    
+    /* Hostname set here should match CN in server certificate */    
+    char *use_host = strndup(hostname, hostlen);
+    if (!use_host) {
+        goto exit;
+    }
+
+    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;
+    }
+
+    if (cfg->cacert_pem_buf != NULL) {
+        mbedtls_x509_crt_init(&tls->cacert);
+        ret = mbedtls_x509_crt_parse(&tls->cacert, 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, NULL);
+    } else {
+        mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
+    }
+    
+    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);
+
+    while ((ret = mbedtls_ssl_handshake(&tls->ssl)) != 0) {
+        if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+            ESP_LOGE(TAG, "mbedtls_ssl_handshake returned -0x%x", -ret);
+            if (cfg->cacert_pem_buf != NULL) {
+                /* This is to check whether handshake failed due to invalid certificate*/
+                verify_certificate(tls);
+            }   
+            goto exit;
+        }
+    }
+    
+    return 0;
+exit:
+    mbedtls_cleanup(tls);
+    return -1;
+}
+
+/**
+ * @brief      Close the TLS connection and free any allocated resources.
+ */
+void esp_tls_conn_delete(esp_tls_t *tls)
+{
+    mbedtls_cleanup(tls);
+    if (tls->sockfd) {
+        close(tls->sockfd);
+    }
+    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)
+{
+    ssize_t ret = mbedtls_ssl_write(&tls->ssl, (unsigned char*) data, datalen);
+    if (ret < 0) {
+        if (ret != MBEDTLS_ERR_SSL_WANT_READ  && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+            ESP_LOGE(TAG, "write error :%d:", ret);
+        }
+    }
+    return ret;
+}
+
+/**
+ * @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;
+    }
+
+    esp_tls_t *tls = (esp_tls_t *)calloc(1, sizeof(esp_tls_t));
+    if (!tls) {
+        close(sockfd);
+        return NULL;
+    }
+    tls->sockfd = sockfd;
+    tls->read = tcp_read;
+    tls->write = tcp_write;
+
+    if (cfg) {
+        if (create_ssl_handle(tls, hostname, hostlen, cfg) != 0) {
+            esp_tls_conn_delete(tls);
+            return NULL;
+        }
+	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;
+}
+
+static int get_port(const char *url, struct http_parser_url *u)
+{
+    if (u->field_data[UF_PORT].len) {
+        return strtol(&url[u->field_data[UF_PORT].off], NULL, 10);
+    } else {
+        if (strncmp(&url[u->field_data[UF_SCHEMA].off], "http", u->field_data[UF_SCHEMA].len) == 0) {
+            return 80;
+        } else if (strncmp(&url[u->field_data[UF_SCHEMA].off], "https", u->field_data[UF_SCHEMA].len) == 0) {
+            return 443;
+        }
+    }
+    return 0;
+}
+
+/**
+ * @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;
+    http_parser_url_init(&u);
+    http_parser_parse_url(url, strlen(url), 0, &u);
+
+    /* 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);
+}

+ 172 - 0
components/esp-tls/esp_tls.h

@@ -0,0 +1,172 @@
+// 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 <stdbool.h>
+#include <sys/socket.h>
+#include <fcntl.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"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @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 */
+ 
+    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 {
+    mbedtls_ssl_context ssl;                                                    /*!< TLS/SSL context */
+ 
+    mbedtls_entropy_context entropy;                                            /*!< mbedTLS entropy context structure */
+ 
+    mbedtls_ctr_drbg_context ctr_drbg;                                          /*!< mbedTLS ctr drbg context structure.
+                                                                                     CTR_DRBG is deterministic random 
+                                                                                     bit generation based on AES-256 */
+ 
+    mbedtls_ssl_config conf;                                                    /*!< TLS/SSL configuration to be shared 
+                                                                                     between mbedtls_ssl_context 
+                                                                                     structures */
+ 
+    mbedtls_net_context server_fd;                                              /*!< mbedTLS wrapper type for sockets */
+ 
+    mbedtls_x509_crt cacert;                                                    /*!< Container for an X.509 certificate */
+ 
+    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
+ *
+ * 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.
+ */
+esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg);
+
+/**
+ * @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, (char *)data, 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, (char *)data, datalen);
+}
+
+/**
+ * @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
+}
+#endif
+
+#endif /* ! _ESP_TLS_H_ */

+ 3 - 0
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

+ 24 - 0
docs/en/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
+

+ 1 - 1
docs/en/api-reference/protocols/index.rst

@@ -5,6 +5,6 @@ Protocols API
    :maxdepth: 1
 
    mDNS <mdns>
-
+   ESP-TLS <esp_tls>
 
 Example code for this API section is provided in :example:`protocols` directory of ESP-IDF examples.

+ 1 - 0
docs/zh_CN/api-reference/protocols/esp_tls.rst

@@ -0,0 +1 @@
+.. include:: ../../../en/api-reference/protocols/esp_tls.rst

+ 0 - 164
examples/protocols/http2_request/components/sh2lib/connectlib.c

@@ -1,164 +0,0 @@
-/* With adaptations by Espressif Systems
- *
- * Copyright (c) 2013 Tatsuhiro Tsujikawa
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-#include <stdlib.h>
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <netdb.h>
-
-#include "connectlib.h"
-
-/* Basic parser from nghttp2/examples/client.c */
-int parse_uri(struct uri *res, const char *uri)
-{
-    /* We only interested in https */
-    size_t len, i, offset;
-    int ipv6addr = 0;
-    memset(res, 0, sizeof(struct uri));
-    len = strlen(uri);
-    if (len < 9 || memcmp("https://", uri, 8) != 0) {
-        return -1;
-    }
-    offset = 8;
-    res->host = res->hostport = &uri[offset];
-    res->hostlen = 0;
-    if (uri[offset] == '[') {
-        /* IPv6 literal address */
-        ++offset;
-        ++res->host;
-        ipv6addr = 1;
-        for (i = offset; i < len; ++i) {
-            if (uri[i] == ']') {
-                res->hostlen = i - offset;
-                offset = i + 1;
-                break;
-            }
-        }
-    } else {
-        const char delims[] = ":/?#";
-        for (i = offset; i < len; ++i) {
-            if (strchr(delims, uri[i]) != NULL) {
-                break;
-            }
-        }
-        res->hostlen = i - offset;
-        offset = i;
-    }
-    if (res->hostlen == 0) {
-        return -1;
-    }
-    /* Assuming https */
-    res->port = 443;
-    if (offset < len) {
-        if (uri[offset] == ':') {
-            /* port */
-            const char delims[] = "/?#";
-            int port = 0;
-            ++offset;
-            for (i = offset; i < len; ++i) {
-                if (strchr(delims, uri[i]) != NULL) {
-                    break;
-                }
-                if ('0' <= uri[i] && uri[i] <= '9') {
-                    port *= 10;
-                    port += uri[i] - '0';
-                    if (port > 65535) {
-                        return -1;
-                    }
-                } else {
-                    return -1;
-                }
-            }
-            if (port == 0) {
-                return -1;
-            }
-            offset = i;
-            res->port = (uint16_t)port;
-        }
-    }
-    res->hostportlen = (size_t)(uri + offset + ipv6addr - res->host);
-    for (i = offset; i < len; ++i) {
-        if (uri[i] == '#') {
-            break;
-        }
-    }
-    if (i - offset == 0) {
-        res->path = "/";
-        res->pathlen = 1;
-    } else {
-        res->path = &uri[offset];
-        res->pathlen = i - offset;
-    }
-    return 0;
-}
-
-int connect_to_host(const char *host, size_t hostlen, uint16_t port)
-{
-    int ret;
-    struct addrinfo hints = {
-        .ai_family = AF_UNSPEC,
-        .ai_socktype = SOCK_STREAM,
-    };
-
-    char service[6];
-    snprintf(service, sizeof(service), "%u", port);
-
-    char *use_host = calloc(1, hostlen + 1);
-    if (!use_host) {
-         return -1;
-    }
-    strncpy(use_host, host, hostlen);
-
-    struct addrinfo *res;
-    ret = getaddrinfo(use_host, service, &hints, &res);
-    if (ret) {
-        free(use_host);
-        return -1;
-    }
-    free(use_host);
-
-    ret = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-    if (ret < 0) {
-        goto err_freeaddr;
-    }
-
-    int fd = ret;
-    ret = connect(fd, res->ai_addr, res->ai_addrlen);
-    if (ret < 0) {
-        goto err_freesocket;
-    }
-
-    return fd;
-
-err_freesocket:
-    close(fd);
-err_freeaddr:
-    freeaddrinfo(res);
-    return -1;
-}
-

+ 0 - 34
examples/protocols/http2_request/components/sh2lib/connectlib.h

@@ -1,34 +0,0 @@
-// Copyright 2017 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_EXAMPLE_CONNECT_LIB_H_
-#define __ESP_EXAMPLE_CONNECT_LIB_H_
-
-struct uri {
-    const char *host;
-    /* In this program, path contains query component as well. */
-    const char *path;
-    size_t pathlen;
-    const char *hostport;
-    size_t hostlen;
-    size_t hostportlen;
-    uint16_t port;
-};
-
-/* connect() to a TCP host, return socket descriptor */
-int connect_to_host(const char *host, size_t hostlen, uint16_t port);
-
-/* Parse a URI into its components */
-int parse_uri(struct uri *res, const char *uri);
-
-#endif /* ! __ESP_EXAMPLE_CONNECT_LIB_H_ */

+ 15 - 74
examples/protocols/http2_request/components/sh2lib/sh2lib.c

@@ -17,55 +17,16 @@
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
-#include <fcntl.h>
 #include <ctype.h>
 #include <netdb.h>
 #include <esp_log.h>
 
-#include "connectlib.h"
 #include "sh2lib.h"
 
 static const char *TAG = "sh2lib";
 
 #define DBG_FRAME_SEND 1
 
-/* SSL connection on the TCP socket that is already connected */
-static int do_ssl_connect(struct sh2lib_handle *hd, int sockfd, const char *hostname)
-{
-    SSL_CTX *ssl_ctx = SSL_CTX_new(TLSv1_2_client_method());
-    if (!ssl_ctx) {
-        return -1;
-    }
-
-    unsigned char vector[] = "\x02h2";
-    SSL_CTX_set_alpn_protos(ssl_ctx, vector, strlen((char *)vector));
-    SSL *ssl = SSL_new(ssl_ctx);
-    if (!ssl) {
-        SSL_CTX_free(ssl_ctx);
-        return -1;
-    }
-
-    SSL_set_tlsext_host_name(ssl, hostname);
-    SSL_set_fd(ssl, sockfd);
-    int ret = SSL_connect(ssl);
-    if (ret < 1) {
-        int err = SSL_get_error(ssl, ret);
-        ESP_LOGE(TAG, "[ssl-connect] Failed SSL handshake ret=%d error=%d", ret, err);
-        SSL_CTX_free(ssl_ctx);
-        SSL_free(ssl);
-        return -1;
-    }
-    hd->ssl_ctx = ssl_ctx;
-    hd->ssl = ssl;
-    hd->sockfd = sockfd;
-    hd->hostname = strdup(hostname);
-
-    int flags = fcntl(hd->sockfd, F_GETFL, 0);
-    fcntl(hd->sockfd, F_SETFL, flags | O_NONBLOCK);
-
-    return 0;
-}
-
 /*
  * The implementation of nghttp2_send_callback type. Here we write
  * |data| with size |length| to the network and return the number of
@@ -75,10 +36,9 @@ 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 = SSL_write(hd->ssl, data, (int)length);
+    int rv = esp_tls_conn_write(hd->http2_tls, data, length);
     if (rv <= 0) {
-        int err = SSL_get_error(hd->ssl, rv);
-        if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
+        if (rv == MBEDTLS_ERR_SSL_WANT_READ || rv == MBEDTLS_ERR_SSL_WANT_WRITE) {
             rv = NGHTTP2_ERR_WOULDBLOCK;
         } else {
             rv = NGHTTP2_ERR_CALLBACK_FAILURE;
@@ -128,10 +88,9 @@ static ssize_t callback_recv(nghttp2_session *session, uint8_t *buf,
 {
     struct sh2lib_handle *hd = user_data;
     int rv;
-    rv = SSL_read(hd->ssl, buf, (int)length);
+    rv = esp_tls_conn_read(hd->http2_tls, (char *)buf, (int)length);
     if (rv < 0) {
-        int err = SSL_get_error(hd->ssl, rv);
-        if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
+        if (rv == MBEDTLS_ERR_SSL_WANT_READ || rv == MBEDTLS_ERR_SSL_WANT_WRITE) {
             rv = NGHTTP2_ERR_WOULDBLOCK;
         } else {
             rv = NGHTTP2_ERR_CALLBACK_FAILURE;
@@ -281,24 +240,12 @@ 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 uri res;
-    /* Parse the URI */
-    if (parse_uri(&res, uri) != 0) {
-        ESP_LOGE(TAG, "[sh2-connect] Failed to parse URI");
-        return -1;
-    }
-
-    /* TCP connection with the server */
-    int sockfd = connect_to_host(res.host, res.hostlen, res.port);
-    if (sockfd < 0) {
-        ESP_LOGE(TAG, "[sh2-connect] Failed to connect to %s", uri);
-        return -1;
-    }
-
-    /* SSL Connection on the socket */
-    if (do_ssl_connect(hd, sockfd, res.host) != 0) {
-        ESP_LOGE(TAG, "[sh2-connect] SSL Handshake failed with %s", uri);
+    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;
     }
 
@@ -320,17 +267,9 @@ void sh2lib_free(struct sh2lib_handle *hd)
         nghttp2_session_del(hd->http2_sess);
         hd->http2_sess = NULL;
     }
-    if (hd->ssl) {
-        SSL_free(hd->ssl);
-        hd->ssl = NULL;
-    }
-    if (hd->ssl_ctx) {
-        SSL_CTX_free(hd->ssl_ctx);
-        hd->ssl_ctx = NULL;
-    }
-    if (hd->sockfd) {
-        close(hd->sockfd);
-        hd->ssl_ctx = 0;
+    if (hd->http2_tls) {
+	esp_tls_conn_delete(hd->http2_tls);
+        hd->http2_tls = NULL;
     }
     if (hd->hostname) {
         free(hd->hostname);
@@ -346,11 +285,13 @@ int sh2lib_execute(struct sh2lib_handle *hd)
         ESP_LOGE(TAG, "[sh2-execute] HTTP2 session send failed %d", ret);
         return -1;
     }
+
     ret = nghttp2_session_recv(hd->http2_sess);
     if (ret != 0) {
         ESP_LOGE(TAG, "[sh2-execute] HTTP2 session recv failed %d", ret);
         return -1;
     }
+
     return 0;
 }
 

+ 2 - 6
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 <openssl/ssl.h>
+#include "esp_tls.h"
 #include <nghttp2/nghttp2.h>
 
 /*
@@ -33,14 +33,10 @@
  * @brief Handle for working with sh2lib APIs
  */
 struct sh2lib_handle {
-    /* Ideally, CTX is per-program, so we could potentially take it out of this
-     * per-connection structure
-     */
-    SSL_CTX         *ssl_ctx;      /*!< Pointer to the SSL context */
-    SSL             *ssl;          /*!< Pointer to the SSL handle */
     nghttp2_session *http2_sess;   /*!< Pointer to the HTTP2 session handle */
     int              sockfd;       /*!< Socket file descriptor */
     char            *hostname;     /*!< The hostname we are connected to */
+    struct esp_tls  *http2_tls;    /*!< Pointer to the TLS session handle */
 };
 
 /** Flag indicating receive stream is reset */

+ 9 - 0
examples/protocols/https_mbedtls/Makefile

@@ -0,0 +1,9 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := https-mbedtls
+
+include $(IDF_PATH)/make/project.mk
+

+ 17 - 0
examples/protocols/https_mbedtls/main/Kconfig.projbuild

@@ -0,0 +1,17 @@
+menu "Example Configuration"
+
+config WIFI_SSID
+    string "WiFi SSID"
+	default "myssid"
+	help
+		SSID (network name) for the example to connect to.
+
+config WIFI_PASSWORD
+    string "WiFi Password"
+	default "mypassword"
+	help
+		WiFi password (WPA or WPA2) for the example to use.
+
+		Can be left blank if the network has no security set.
+
+endmenu

+ 10 - 0
examples/protocols/https_mbedtls/main/component.mk

@@ -0,0 +1,10 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
+
+# embed files from the "certs" directory as binary data symbols
+# in the app
+COMPONENT_EMBED_TXTFILES := server_root_cert.pem
+
+

+ 341 - 0
examples/protocols/https_mbedtls/main/https_mbedtls_example_main.c

@@ -0,0 +1,341 @@
+/* HTTPS GET Example using plain mbedTLS sockets
+ *
+ * Contacts the howsmyssl.com API via TLS v1.2 and reads a JSON
+ * response.
+ *
+ * Adapted from the ssl_client1 example in mbedtls.
+ *
+ * Original Copyright (C) 2006-2016, ARM Limited, All Rights Reserved, Apache 2.0 License.
+ * Additions Copyright (C) Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD, Apache 2.0 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 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 <string.h>
+#include <stdlib.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/event_groups.h"
+#include "esp_wifi.h"
+#include "esp_event_loop.h"
+#include "esp_log.h"
+#include "esp_system.h"
+#include "nvs_flash.h"
+
+#include "lwip/err.h"
+#include "lwip/sockets.h"
+#include "lwip/sys.h"
+#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"
+
+/* The examples use simple WiFi configuration that you can set via
+   'make menuconfig'.
+
+   If you'd rather not, just change the below entries to strings with
+   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
+*/
+#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
+#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
+
+/* FreeRTOS event group to signal when we are connected & ready to make a request */
+static EventGroupHandle_t wifi_event_group;
+
+/* The event group allows multiple bits for each event,
+   but we only care about one event - are we connected
+   to the AP with an IP? */
+const int CONNECTED_BIT = BIT0;
+
+/* Constants that aren't configurable in menuconfig */
+#define WEB_SERVER "www.howsmyssl.com"
+#define WEB_PORT "443"
+#define WEB_URL "https://www.howsmyssl.com/a/check"
+
+static const char *TAG = "example";
+
+static const char *REQUEST = "GET " WEB_URL " HTTP/1.0\r\n"
+    "Host: "WEB_SERVER"\r\n"
+    "User-Agent: esp-idf/1.0 esp32\r\n"
+    "\r\n";
+
+/* Root cert for howsmyssl.com, taken from server_root_cert.pem
+
+   The PEM file was extracted from the output of this command:
+   openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
+
+   The CA root cert is the last cert given in the chain of certs.
+
+   To embed it in the app binary, the PEM file is named
+   in the component.mk COMPONENT_EMBED_TXTFILES variable.
+*/
+extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
+extern const uint8_t server_root_cert_pem_end[]   asm("_binary_server_root_cert_pem_end");
+
+static esp_err_t event_handler(void *ctx, system_event_t *event)
+{
+    switch(event->event_id) {
+    case SYSTEM_EVENT_STA_START:
+        esp_wifi_connect();
+        break;
+    case SYSTEM_EVENT_STA_GOT_IP:
+        xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
+        break;
+    case SYSTEM_EVENT_STA_DISCONNECTED:
+        /* This is a workaround as ESP32 WiFi libs don't currently
+           auto-reassociate. */
+        esp_wifi_connect();
+        xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
+        break;
+    default:
+        break;
+    }
+    return ESP_OK;
+}
+
+static void initialise_wifi(void)
+{
+    tcpip_adapter_init();
+    wifi_event_group = xEventGroupCreate();
+    ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
+    ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
+    wifi_config_t wifi_config = {
+        .sta = {
+            .ssid = EXAMPLE_WIFI_SSID,
+            .password = EXAMPLE_WIFI_PASS,
+        },
+    };
+    ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
+    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
+    ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
+    ESP_ERROR_CHECK( esp_wifi_start() );
+}
+
+static void https_get_task(void *pvParameters)
+{
+    char buf[512];
+    int ret, flags, len;
+
+    mbedtls_entropy_context entropy;
+    mbedtls_ctr_drbg_context ctr_drbg;
+    mbedtls_ssl_context ssl;
+    mbedtls_x509_crt cacert;
+    mbedtls_ssl_config conf;
+    mbedtls_net_context server_fd;
+
+    mbedtls_ssl_init(&ssl);
+    mbedtls_x509_crt_init(&cacert);
+    mbedtls_ctr_drbg_init(&ctr_drbg);
+    ESP_LOGI(TAG, "Seeding the random number generator");
+
+    mbedtls_ssl_config_init(&conf);
+
+    mbedtls_entropy_init(&entropy);
+    if((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
+                                    NULL, 0)) != 0)
+    {
+        ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret);
+        abort();
+    }
+
+    ESP_LOGI(TAG, "Loading the CA root certificate...");
+
+    ret = mbedtls_x509_crt_parse(&cacert, server_root_cert_pem_start,
+                                 server_root_cert_pem_end-server_root_cert_pem_start);
+
+    if(ret < 0)
+    {
+        ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
+        abort();
+    }
+
+    ESP_LOGI(TAG, "Setting hostname for TLS session...");
+
+     /* Hostname set here should match CN in server certificate */
+    if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0)
+    {
+        ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret);
+        abort();
+    }
+
+    ESP_LOGI(TAG, "Setting up the SSL/TLS structure...");
+
+    if((ret = mbedtls_ssl_config_defaults(&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;
+    }
+
+    /* MBEDTLS_SSL_VERIFY_OPTIONAL is bad for security, in this example it will print
+       a warning if CA verification fails but it will continue to connect.
+
+       You should consider using MBEDTLS_SSL_VERIFY_REQUIRED in your own code.
+    */
+    mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
+    mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
+    mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
+#ifdef CONFIG_MBEDTLS_DEBUG
+    mbedtls_esp_enable_debug_log(&conf, 4);
+#endif
+
+    if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0)
+    {
+        ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x\n\n", -ret);
+        goto exit;
+    }
+
+    while(1) {
+        /* Wait for the callback to set the CONNECTED_BIT in the
+           event group.
+        */
+        xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
+                            false, true, portMAX_DELAY);
+        ESP_LOGI(TAG, "Connected to AP");
+
+        mbedtls_net_init(&server_fd);
+
+        ESP_LOGI(TAG, "Connecting to %s:%s...", WEB_SERVER, WEB_PORT);
+
+        if ((ret = mbedtls_net_connect(&server_fd, WEB_SERVER,
+                                      WEB_PORT, MBEDTLS_NET_PROTO_TCP)) != 0)
+        {
+            ESP_LOGE(TAG, "mbedtls_net_connect returned -%x", -ret);
+            goto exit;
+        }
+
+        ESP_LOGI(TAG, "Connected.");
+
+        mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
+
+        ESP_LOGI(TAG, "Performing the SSL/TLS handshake...");
+
+        while ((ret = mbedtls_ssl_handshake(&ssl)) != 0)
+        {
+            if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
+            {
+                ESP_LOGE(TAG, "mbedtls_ssl_handshake returned -0x%x", -ret);
+                goto exit;
+            }
+        }
+
+        ESP_LOGI(TAG, "Verifying peer X.509 certificate...");
+
+        if ((flags = mbedtls_ssl_get_verify_result(&ssl)) != 0)
+        {
+            /* In real life, we probably want to close connection if ret != 0 */
+            ESP_LOGW(TAG, "Failed to verify peer certificate!");
+            bzero(buf, sizeof(buf));
+            mbedtls_x509_crt_verify_info(buf, sizeof(buf), "  ! ", flags);
+            ESP_LOGW(TAG, "verification info: %s", buf);
+        }
+        else {
+            ESP_LOGI(TAG, "Certificate verified.");
+        }
+
+        ESP_LOGI(TAG, "Cipher suite is %s", mbedtls_ssl_get_ciphersuite(&ssl));
+
+        ESP_LOGI(TAG, "Writing HTTP request...");
+
+        size_t written_bytes = 0;
+        do {
+            ret = mbedtls_ssl_write(&ssl,
+                                    (const unsigned char *)REQUEST + written_bytes,
+                                    strlen(REQUEST) - written_bytes);
+            if (ret >= 0) {
+                ESP_LOGI(TAG, "%d bytes written", ret);
+                written_bytes += ret;
+            } else if (ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != MBEDTLS_ERR_SSL_WANT_READ) {
+                ESP_LOGE(TAG, "mbedtls_ssl_write returned -0x%x", -ret);
+                goto exit;
+            }
+        } while(written_bytes < strlen(REQUEST));
+
+        ESP_LOGI(TAG, "Reading HTTP response...");
+
+        do
+        {
+            len = sizeof(buf) - 1;
+            bzero(buf, sizeof(buf));
+            ret = mbedtls_ssl_read(&ssl, (unsigned char *)buf, len);
+
+            if(ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
+                continue;
+
+            if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
+                ret = 0;
+                break;
+            }
+
+            if(ret < 0)
+            {
+                ESP_LOGE(TAG, "mbedtls_ssl_read returned -0x%x", -ret);
+                break;
+            }
+
+            if(ret == 0)
+            {
+                ESP_LOGI(TAG, "connection closed");
+                break;
+            }
+
+            len = ret;
+            ESP_LOGD(TAG, "%d bytes read", len);
+            /* Print response directly to stdout as it is read */
+            for(int i = 0; i < len; i++) {
+                putchar(buf[i]);
+            }
+        } while(1);
+
+        mbedtls_ssl_close_notify(&ssl);
+
+    exit:
+        mbedtls_ssl_session_reset(&ssl);
+        mbedtls_net_free(&server_fd);
+
+        if(ret != 0)
+        {
+            mbedtls_strerror(ret, buf, 100);
+            ESP_LOGE(TAG, "Last error was: -0x%x - %s", -ret, buf);
+        }
+
+        putchar('\n'); // JSON output doesn't have a newline at end
+
+        static int request_count;
+        ESP_LOGI(TAG, "Completed %d requests", ++request_count);
+
+        for(int countdown = 10; countdown >= 0; countdown--) {
+            ESP_LOGI(TAG, "%d...", countdown);
+            vTaskDelay(1000 / portTICK_PERIOD_MS);
+        }
+        ESP_LOGI(TAG, "Starting again!");
+    }
+}
+
+void app_main()
+{
+    ESP_ERROR_CHECK( nvs_flash_init() );
+    initialise_wifi();
+    xTaskCreate(&https_get_task, "https_get_task", 8192, NULL, 5, NULL);
+}

+ 27 - 0
examples/protocols/https_mbedtls/main/server_root_cert.pem

@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
+MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
+DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
+SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
+GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
+q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
+SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
+Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
+a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
+/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
+AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
+CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
+bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
+c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
+VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
+ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
+MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
+Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
+AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
+uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
+wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
+X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
+PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
+KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
+-----END CERTIFICATE-----

+ 1 - 1
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.

+ 27 - 151
examples/protocols/https_request/main/https_request_example_main.c

@@ -38,14 +38,7 @@
 #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
    'make menuconfig'.
@@ -88,7 +81,7 @@ static const char *REQUEST = "GET " WEB_URL " HTTP/1.0\r\n"
 */
 extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
 extern const uint8_t server_root_cert_pem_end[]   asm("_binary_server_root_cert_pem_end");
-
+    
 static esp_err_t event_handler(void *ctx, system_event_t *event)
 {
     switch(event->event_id) {
@@ -133,78 +126,7 @@ static void initialise_wifi(void)
 static void https_get_task(void *pvParameters)
 {
     char buf[512];
-    int ret, flags, len;
-
-    mbedtls_entropy_context entropy;
-    mbedtls_ctr_drbg_context ctr_drbg;
-    mbedtls_ssl_context ssl;
-    mbedtls_x509_crt cacert;
-    mbedtls_ssl_config conf;
-    mbedtls_net_context server_fd;
-
-    mbedtls_ssl_init(&ssl);
-    mbedtls_x509_crt_init(&cacert);
-    mbedtls_ctr_drbg_init(&ctr_drbg);
-    ESP_LOGI(TAG, "Seeding the random number generator");
-
-    mbedtls_ssl_config_init(&conf);
-
-    mbedtls_entropy_init(&entropy);
-    if((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
-                                    NULL, 0)) != 0)
-    {
-        ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret);
-        abort();
-    }
-
-    ESP_LOGI(TAG, "Loading the CA root certificate...");
-
-    ret = mbedtls_x509_crt_parse(&cacert, server_root_cert_pem_start,
-                                 server_root_cert_pem_end-server_root_cert_pem_start);
-
-    if(ret < 0)
-    {
-        ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
-        abort();
-    }
-
-    ESP_LOGI(TAG, "Setting hostname for TLS session...");
-
-     /* Hostname set here should match CN in server certificate */
-    if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0)
-    {
-        ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret);
-        abort();
-    }
-
-    ESP_LOGI(TAG, "Setting up the SSL/TLS structure...");
-
-    if((ret = mbedtls_ssl_config_defaults(&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;
-    }
-
-    /* MBEDTLS_SSL_VERIFY_OPTIONAL is bad for security, in this example it will print
-       a warning if CA verification fails but it will continue to connect.
-
-       You should consider using MBEDTLS_SSL_VERIFY_REQUIRED in your own code.
-    */
-    mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
-    mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
-    mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
-#ifdef CONFIG_MBEDTLS_DEBUG
-    mbedtls_esp_enable_debug_log(&conf, 4);
-#endif
-
-    if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0)
-    {
-        ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x\n\n", -ret);
-        goto exit;
-    }
+    int ret, len;
 
     while(1) {
         /* Wait for the callback to set the CONNECTED_BIT in the
@@ -213,61 +135,30 @@ static void https_get_task(void *pvParameters)
         xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
                             false, true, portMAX_DELAY);
         ESP_LOGI(TAG, "Connected to AP");
-
-        mbedtls_net_init(&server_fd);
-
-        ESP_LOGI(TAG, "Connecting to %s:%s...", WEB_SERVER, WEB_PORT);
-
-        if ((ret = mbedtls_net_connect(&server_fd, WEB_SERVER,
-                                      WEB_PORT, MBEDTLS_NET_PROTO_TCP)) != 0)
-        {
-            ESP_LOGE(TAG, "mbedtls_net_connect returned -%x", -ret);
+        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,
+        };
+        
+        struct esp_tls *tls = esp_tls_conn_http_new(WEB_URL, &cfg);
+        
+        if(tls != NULL) {
+            ESP_LOGI(TAG, "Connection established...");
+        } else {
+            ESP_LOGE(TAG, "Connection failed...");
             goto exit;
         }
-
-        ESP_LOGI(TAG, "Connected.");
-
-        mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
-
-        ESP_LOGI(TAG, "Performing the SSL/TLS handshake...");
-
-        while ((ret = mbedtls_ssl_handshake(&ssl)) != 0)
-        {
-            if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
-            {
-                ESP_LOGE(TAG, "mbedtls_ssl_handshake returned -0x%x", -ret);
-                goto exit;
-            }
-        }
-
-        ESP_LOGI(TAG, "Verifying peer X.509 certificate...");
-
-        if ((flags = mbedtls_ssl_get_verify_result(&ssl)) != 0)
-        {
-            /* In real life, we probably want to close connection if ret != 0 */
-            ESP_LOGW(TAG, "Failed to verify peer certificate!");
-            bzero(buf, sizeof(buf));
-            mbedtls_x509_crt_verify_info(buf, sizeof(buf), "  ! ", flags);
-            ESP_LOGW(TAG, "verification info: %s", buf);
-        }
-        else {
-            ESP_LOGI(TAG, "Certificate verified.");
-        }
-
-        ESP_LOGI(TAG, "Cipher suite is %s", mbedtls_ssl_get_ciphersuite(&ssl));
-
-        ESP_LOGI(TAG, "Writing HTTP request...");
-
+        
         size_t written_bytes = 0;
         do {
-            ret = mbedtls_ssl_write(&ssl,
-                                    (const unsigned char *)REQUEST + written_bytes,
-                                    strlen(REQUEST) - written_bytes);
+            ret = esp_tls_conn_write(tls, 
+                                     REQUEST + written_bytes, 
+                                     strlen(REQUEST) - written_bytes);
             if (ret >= 0) {
                 ESP_LOGI(TAG, "%d bytes written", ret);
                 written_bytes += ret;
-            } else if (ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != MBEDTLS_ERR_SSL_WANT_READ) {
-                ESP_LOGE(TAG, "mbedtls_ssl_write returned -0x%x", -ret);
+            } else if (ret != MBEDTLS_ERR_SSL_WANT_READ  && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+                ESP_LOGE(TAG, "esp_tls_conn_write  returned 0x%x", ret);
                 goto exit;
             }
         } while(written_bytes < strlen(REQUEST));
@@ -278,19 +169,14 @@ static void https_get_task(void *pvParameters)
         {
             len = sizeof(buf) - 1;
             bzero(buf, sizeof(buf));
-            ret = mbedtls_ssl_read(&ssl, (unsigned char *)buf, len);
-
-            if(ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
+            ret = esp_tls_conn_read(tls, (char *)buf, len);
+            
+            if(ret == MBEDTLS_ERR_SSL_WANT_WRITE  || ret == MBEDTLS_ERR_SSL_WANT_READ)
                 continue;
-
-            if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
-                ret = 0;
-                break;
-            }
-
+            
             if(ret < 0)
-            {
-                ESP_LOGE(TAG, "mbedtls_ssl_read returned -0x%x", -ret);
+           {
+                ESP_LOGE(TAG, "esp_tls_conn_read  returned -0x%x", -ret);
                 break;
             }
 
@@ -308,18 +194,8 @@ static void https_get_task(void *pvParameters)
             }
         } while(1);
 
-        mbedtls_ssl_close_notify(&ssl);
-
     exit:
-        mbedtls_ssl_session_reset(&ssl);
-        mbedtls_net_free(&server_fd);
-
-        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
 
         static int request_count;