From 7f35c4ff3eb1f574f24c38a4bd90fa329dbc60f7 Mon Sep 17 00:00:00 2001 From: Kedar Sovani Date: Sat, 3 Feb 2018 09:01:45 +0530 Subject: [PATCH 01/17] esp-tls: Basic structure Purpose: 1. TLS calls can be too many, and require a user to know the expected behaviour. A simple TLS socket wrapper that can be used in any higher level protocol. 2. Uses OpenSSL compatibility layer, so applications using esp-tls can be built on the host, and it should just work on ESP --- components/esp-tls/component.mk | 3 + components/esp-tls/esp-tls.c | 150 ++++++++++++++++++++++++++++++++ components/esp-tls/esp-tls.h | 36 ++++++++ 3 files changed, 189 insertions(+) create mode 100644 components/esp-tls/component.mk create mode 100644 components/esp-tls/esp-tls.c create mode 100644 components/esp-tls/esp-tls.h diff --git a/components/esp-tls/component.mk b/components/esp-tls/component.mk new file mode 100644 index 000000000..7267d5f37 --- /dev/null +++ b/components/esp-tls/component.mk @@ -0,0 +1,3 @@ +COMPONENT_SRCDIRS := . + +COMPONENT_ADD_INCLUDEDIRS := . diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp-tls.c new file mode 100644 index 000000000..6867272f4 --- /dev/null +++ b/components/esp-tls/esp-tls.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "esp-tls.h" + + +static const char *TAG = "esp-tls"; + +#ifdef ESP_PLATFORM +#include +#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, int port) +{ + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + char *use_host = (char *)calloc(1, hostlen + 1); + if (!use_host) { + return NULL; + } + strncpy(use_host, host, hostlen); + use_host[hostlen] = '\0'; + + char service[6]; + snprintf(service, sizeof(service), "%d", port); + + ESP_LOGD(TAG, "port is :%s: host:%s: strlen %zu\n", service, use_host, hostlen); + struct addrinfo *res; + if (getaddrinfo(use_host, service, &hints, &res)) { + ESP_LOGE(TAG, "couldn't get hostname for :%s:\n", use_host); + free(use_host); + return NULL; + } + free(use_host); + return res; +} + +static int esp_tcp_connect(const char *host, int hostlen, int port) +{ + struct addrinfo *res = resolve_host_name(host, hostlen, port); + 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; + ret = connect(fd, res->ai_addr, res->ai_addrlen); + if (ret < 0) { + goto err_freesocket; + } + + freeaddrinfo(res); + return fd; + +err_freesocket: + close(fd); +err_freeaddr: + freeaddrinfo(res); + return -1; +} + +static int create_ssl_handle(struct esp_tls *tls) +{ + int ret; + + SSL_CTX *ssl_ctx = SSL_CTX_new(TLSv1_2_client_method()); + if (!ssl_ctx) { + return -1; + } +#ifdef __APPLE__ + SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); +#endif + SSL *ssl = SSL_new(ssl_ctx); + if (!ssl) { + SSL_CTX_free(ssl_ctx); + return -1; + } + + SSL_set_fd(ssl, tls->sockfd); + ret = SSL_connect(ssl); + if (ret < 1) { + ESP_LOGE(TAG, "SSL handshake failed"); + SSL_free(ssl); + SSL_CTX_free(ssl_ctx); + return -1; + } + + tls->ctx = ssl_ctx; + tls->ssl = ssl; + return 0; +} + +void esp_tls_conn_delete(struct esp_tls *tls) +{ + if (!tls) { + return; + } + if (tls->ssl) { + SSL_free(tls->ssl); + } + if (tls->ctx) { + SSL_CTX_free(tls->ctx); + } + if (tls->sockfd) { + close(tls->sockfd); + } + free(tls); +}; + +struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, bool is_tls) +{ + 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)); + if (!tls) { + close(sockfd); + return NULL; + } + tls->sockfd = sockfd; + + if (is_tls) { + if (create_ssl_handle(tls) != 0) { + esp_tls_conn_delete(tls); + return NULL; + } + } + return tls; +} + diff --git a/components/esp-tls/esp-tls.h b/components/esp-tls/esp-tls.h new file mode 100644 index 000000000..655edc08b --- /dev/null +++ b/components/esp-tls/esp-tls.h @@ -0,0 +1,36 @@ +#ifndef _ESP_TLS_H_ +#define _ESP_TLS_H_ + +#include +#include +#include + +struct esp_tls { + SSL_CTX *ctx; + SSL *ssl; + int sockfd; +}; + +struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, bool is_tls); + +static inline ssize_t esp_tls_conn_write(struct esp_tls *tls, const char *data, size_t datalen) +{ + if (tls->ssl) { + return SSL_write(tls->ssl, data, datalen); + } else { + return send(tls->sockfd, data, datalen, 0); + } +} + +static inline ssize_t esp_tls_conn_read(struct esp_tls *tls, char *data, size_t datalen) +{ + if (tls->ssl) { + return SSL_read(tls->ssl, data, datalen); + } else { + return recv(tls->sockfd, data, datalen, 0); + } +} + +void esp_tls_conn_delete(struct esp_tls *tls); + +#endif /* ! _ESP_TLS_H_ */ From 306d59d32c0a768b7d8089068ab5972324ddebb6 Mon Sep 17 00:00:00 2001 From: Kedar Sovani Date: Fri, 17 Nov 2017 15:49:12 +0530 Subject: [PATCH 02/17] Make read/write function pointers as per Ivan's feedback --- components/esp-tls/esp-tls.c | 24 ++++++++++++++++++++++++ components/esp-tls/esp-tls.h | 20 +++++++------------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp-tls.c index 6867272f4..032a91f7f 100644 --- a/components/esp-tls/esp-tls.c +++ b/components/esp-tls/esp-tls.c @@ -47,6 +47,16 @@ static struct addrinfo *resolve_host_name(const char *host, size_t hostlen, int return res; } +static ssize_t tcp_read(struct esp_tls *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) +{ + return SSL_read(tls->ssl, data, datalen); +} + static int esp_tcp_connect(const char *host, int hostlen, int port) { struct addrinfo *res = resolve_host_name(host, hostlen, port); @@ -125,6 +135,16 @@ 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) +{ + return send(tls->sockfd, data, datalen, 0); +} + +static ssize_t tls_write(struct esp_tls *tls, const char *data, size_t datalen) +{ + return SSL_write(tls->ssl, data, datalen); +} + struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, bool is_tls) { int sockfd = esp_tcp_connect(hostname, hostlen, port); @@ -138,12 +158,16 @@ struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, bo return NULL; } tls->sockfd = sockfd; + tls->read = tcp_read; + tls->write = tcp_write; if (is_tls) { if (create_ssl_handle(tls) != 0) { esp_tls_conn_delete(tls); return NULL; } + tls->read = tls_read; + tls->write = tls_write; } return tls; } diff --git a/components/esp-tls/esp-tls.h b/components/esp-tls/esp-tls.h index 655edc08b..bff2eda4d 100644 --- a/components/esp-tls/esp-tls.h +++ b/components/esp-tls/esp-tls.h @@ -6,29 +6,23 @@ #include struct esp_tls { - SSL_CTX *ctx; - SSL *ssl; - int sockfd; + 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); }; struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, bool is_tls); static inline ssize_t esp_tls_conn_write(struct esp_tls *tls, const char *data, size_t datalen) { - if (tls->ssl) { - return SSL_write(tls->ssl, data, datalen); - } else { - return send(tls->sockfd, data, datalen, 0); - } + return tls->write(tls, data, datalen); } static inline ssize_t esp_tls_conn_read(struct esp_tls *tls, char *data, size_t datalen) { - if (tls->ssl) { - return SSL_read(tls->ssl, data, datalen); - } else { - return recv(tls->sockfd, data, datalen, 0); - } + return tls->read(tls, data, datalen); } void esp_tls_conn_delete(struct esp_tls *tls); From e45024e0884b328de47b64de56105f2d381f7873 Mon Sep 17 00:00:00 2001 From: Kedar Sovani Date: Thu, 23 Nov 2017 17:20:38 +0530 Subject: [PATCH 03/17] Don't use 'port' number for DNS query - In some cases where the HTTP URL contains the port number (http://hostname:334), the DNS querier fails to resolve the hostname. - Hence we have to populate the port number ourselves. - This can only be done based on whether we get an IPv4 or IPv6 address. --- components/esp-tls/esp-tls.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp-tls.c index 032a91f7f..21d9e8123 100644 --- a/components/esp-tls/esp-tls.c +++ b/components/esp-tls/esp-tls.c @@ -19,7 +19,7 @@ static const char *TAG = "esp-tls"; #define ESP_LOGE(TAG, ...) printf(__VA_ARGS__); #endif -static struct addrinfo *resolve_host_name(const char *host, size_t hostlen, int port) +static struct addrinfo *resolve_host_name(const char *host, size_t hostlen) { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); @@ -33,12 +33,9 @@ static struct addrinfo *resolve_host_name(const char *host, size_t hostlen, int strncpy(use_host, host, hostlen); use_host[hostlen] = '\0'; - char service[6]; - snprintf(service, sizeof(service), "%d", port); - - ESP_LOGD(TAG, "port is :%s: host:%s: strlen %zu\n", service, use_host, hostlen); + ESP_LOGD(TAG, "host:%s: strlen %zu\n", use_host, hostlen); struct addrinfo *res; - if (getaddrinfo(use_host, service, &hints, &res)) { + if (getaddrinfo(use_host, NULL, &hints, &res)) { ESP_LOGE(TAG, "couldn't get hostname for :%s:\n", use_host); free(use_host); return NULL; @@ -59,7 +56,7 @@ static ssize_t tls_read(struct esp_tls *tls, char *data, size_t datalen) static int esp_tcp_connect(const char *host, int hostlen, int port) { - struct addrinfo *res = resolve_host_name(host, hostlen, port); + struct addrinfo *res = resolve_host_name(host, hostlen); if (!res) { return -1; } @@ -68,9 +65,24 @@ static int esp_tcp_connect(const char *host, int hostlen, int port) if (ret < 0) { goto err_freeaddr; } - int fd = ret; - ret = connect(fd, res->ai_addr, res->ai_addrlen); + + void *addr_ptr; + if (res->ai_family == AF_INET) { + struct sockaddr_in *p = res->ai_addr; + p->sin_port = htons(port); + addr_ptr = p; + } else if (res->ai_family == AF_INET6) { + struct sockaddr_in6 *p = 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; } From adbcaf893857ba87d0c39cdd5b994083621c72ac Mon Sep 17 00:00:00 2001 From: Anuj Deshpande Date: Thu, 23 Nov 2017 18:22:07 +0530 Subject: [PATCH 04/17] Cast to remove warnings --- components/esp-tls/esp-tls.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp-tls.c index 21d9e8123..8822a3dcf 100644 --- a/components/esp-tls/esp-tls.c +++ b/components/esp-tls/esp-tls.c @@ -69,11 +69,11 @@ static int esp_tcp_connect(const char *host, int hostlen, int port) void *addr_ptr; if (res->ai_family == AF_INET) { - struct sockaddr_in *p = res->ai_addr; + 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 = res->ai_addr; + struct sockaddr_in6 *p = (struct sockaddr_in6*) res->ai_addr; p->sin6_port = htons(port); p->sin6_family = AF_INET6; addr_ptr = p; From eb051fe72f3283d4315737b3adc98a650a2900e8 Mon Sep 17 00:00:00 2001 From: Kedar Sovani Date: Thu, 30 Nov 2017 11:34:54 +0530 Subject: [PATCH 05/17] Minor fixes --- components/esp-tls/esp-tls.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp-tls.c index 8822a3dcf..9906fdbf7 100644 --- a/components/esp-tls/esp-tls.c +++ b/components/esp-tls/esp-tls.c @@ -26,12 +26,10 @@ static struct addrinfo *resolve_host_name(const char *host, size_t hostlen) hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - char *use_host = (char *)calloc(1, hostlen + 1); + char *use_host = strndup(host, hostlen); if (!use_host) { return NULL; } - strncpy(use_host, host, hostlen); - use_host[hostlen] = '\0'; ESP_LOGD(TAG, "host:%s: strlen %zu\n", use_host, hostlen); struct addrinfo *res; From 1c72c8d1260036cfd35a1827cf6b2101068ed6bd Mon Sep 17 00:00:00 2001 From: Kedar Sovani Date: Thu, 30 Nov 2017 11:35:34 +0530 Subject: [PATCH 06/17] Supports TLS with HTTP2 client --- components/esp-tls/esp-tls.c | 19 +++++++++++++++---- components/esp-tls/esp-tls.h | 28 +++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp-tls.c index 9906fdbf7..59ccd1a81 100644 --- a/components/esp-tls/esp-tls.c +++ b/components/esp-tls/esp-tls.c @@ -95,7 +95,7 @@ err_freeaddr: return -1; } -static int create_ssl_handle(struct esp_tls *tls) +static int create_ssl_handle(struct esp_tls *tls, const char *hostname, size_t hostlen, struct esp_tls_cfg *cfg) { int ret; @@ -108,12 +108,23 @@ static int create_ssl_handle(struct esp_tls *tls) SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); #endif + if (cfg->alpn_protos) { + SSL_CTX_set_alpn_protos(ssl_ctx, cfg->alpn_protos, strlen((char *)cfg->alpn_protos)); + } SSL *ssl = SSL_new(ssl_ctx); if (!ssl) { SSL_CTX_free(ssl_ctx); return -1; } + char *use_host = strndup(hostname, hostlen); + if (!use_host) { + SSL_CTX_free(ssl_ctx); + return -1; + } + SSL_set_tlsext_host_name(ssl, use_host); + free(use_host); + SSL_set_fd(ssl, tls->sockfd); ret = SSL_connect(ssl); if (ret < 1) { @@ -155,7 +166,7 @@ static ssize_t tls_write(struct esp_tls *tls, const char *data, size_t datalen) return SSL_write(tls->ssl, data, datalen); } -struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, bool is_tls) +struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, struct esp_tls_cfg *cfg) { int sockfd = esp_tcp_connect(hostname, hostlen, port); if (sockfd < 0) { @@ -171,8 +182,8 @@ struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, bo tls->read = tcp_read; tls->write = tcp_write; - if (is_tls) { - if (create_ssl_handle(tls) != 0) { + if (cfg) { + if (create_ssl_handle(tls, hostname, hostlen, cfg) != 0) { esp_tls_conn_delete(tls); return NULL; } diff --git a/components/esp-tls/esp-tls.h b/components/esp-tls/esp-tls.h index bff2eda4d..08cc63cf2 100644 --- a/components/esp-tls/esp-tls.h +++ b/components/esp-tls/esp-tls.h @@ -5,6 +5,22 @@ #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; +}; + struct esp_tls { SSL_CTX *ctx; SSL *ssl; @@ -13,7 +29,13 @@ struct esp_tls { ssize_t (*write)(struct esp_tls *tls, const char *data, size_t datalen); }; -struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, bool is_tls); +/* + * + * 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. + */ +struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, struct esp_tls_cfg *cfg); static inline ssize_t esp_tls_conn_write(struct esp_tls *tls, const char *data, size_t datalen) { @@ -27,4 +49,8 @@ static inline ssize_t esp_tls_conn_read(struct esp_tls *tls, char *data, size_t void esp_tls_conn_delete(struct esp_tls *tls); +#ifdef __cplusplus +} +#endif + #endif /* ! _ESP_TLS_H_ */ From 433bd8c91c562d7e81936fc116da75eb76991dd6 Mon Sep 17 00:00:00 2001 From: Kedar Sovani Date: Thu, 30 Nov 2017 11:34:54 +0530 Subject: [PATCH 07/17] Minor fixes --- components/esp-tls/esp-tls.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp-tls.c index 59ccd1a81..287bfdaf5 100644 --- a/components/esp-tls/esp-tls.c +++ b/components/esp-tls/esp-tls.c @@ -67,11 +67,11 @@ static int esp_tcp_connect(const char *host, int hostlen, int port) void *addr_ptr; if (res->ai_family == AF_INET) { - struct sockaddr_in *p = (struct sockaddr_in*)res->ai_addr; + 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; + struct sockaddr_in6 *p = (struct sockaddr_in6 *)res->ai_addr; p->sin6_port = htons(port); p->sin6_family = AF_INET6; addr_ptr = p; From 070884fc2e2c075f4168ffef42f86e254834a967 Mon Sep 17 00:00:00 2001 From: Kedar Sovani Date: Mon, 18 Dec 2017 16:40:39 +0530 Subject: [PATCH 08/17] Include error log in case of error --- components/esp-tls/esp-tls.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp-tls.c index 287bfdaf5..3ed8939c0 100644 --- a/components/esp-tls/esp-tls.c +++ b/components/esp-tls/esp-tls.c @@ -49,7 +49,15 @@ static ssize_t tcp_read(struct esp_tls *tls, char *data, size_t datalen) static ssize_t tls_read(struct esp_tls *tls, char *data, size_t datalen) { - return SSL_read(tls->ssl, data, 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); + } + return -err; + } + return ret; } static int esp_tcp_connect(const char *host, int hostlen, int port) @@ -163,7 +171,15 @@ static ssize_t tcp_write(struct esp_tls *tls, const char *data, size_t datalen) static ssize_t tls_write(struct esp_tls *tls, const char *data, size_t datalen) { - return SSL_write(tls->ssl, data, 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); + } + return -err; + } + return ret; } struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, struct esp_tls_cfg *cfg) From 8211a1620751d1941b2b361b53be9ab2cc23e846 Mon Sep 17 00:00:00 2001 From: Kedar Sovani Date: Sun, 4 Feb 2018 10:02:31 +0530 Subject: [PATCH 09/17] Use esp-tls in the http2 example --- components/esp-tls/esp-tls.c | 26 +++ components/esp-tls/esp-tls.h | 3 + .../components/sh2lib/connectlib.c | 164 ------------------ .../components/sh2lib/connectlib.h | 34 ---- .../http2_request/components/sh2lib/sh2lib.c | 53 ++---- .../http2_request/components/sh2lib/sh2lib.h | 6 +- 6 files changed, 47 insertions(+), 239 deletions(-) delete mode 100644 examples/protocols/http2_request/components/sh2lib/connectlib.c delete mode 100644 examples/protocols/http2_request/components/sh2lib/connectlib.h diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp-tls.c index 3ed8939c0..d7599ed53 100644 --- a/components/esp-tls/esp-tls.c +++ b/components/esp-tls/esp-tls.c @@ -7,6 +7,7 @@ #include #include +#include #include "esp-tls.h" @@ -209,3 +210,28 @@ struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, st 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; +} + +struct esp_tls *esp_tls_conn_http_new(const char *url, struct esp_tls_cfg *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); +} diff --git a/components/esp-tls/esp-tls.h b/components/esp-tls/esp-tls.h index 08cc63cf2..f921a5e6f 100644 --- a/components/esp-tls/esp-tls.h +++ b/components/esp-tls/esp-tls.h @@ -37,6 +37,9 @@ struct esp_tls { */ struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, struct esp_tls_cfg *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) { return tls->write(tls, data, datalen); diff --git a/examples/protocols/http2_request/components/sh2lib/connectlib.c b/examples/protocols/http2_request/components/sh2lib/connectlib.c deleted file mode 100644 index 2fe42cbdc..000000000 --- a/examples/protocols/http2_request/components/sh2lib/connectlib.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} - diff --git a/examples/protocols/http2_request/components/sh2lib/connectlib.h b/examples/protocols/http2_request/components/sh2lib/connectlib.h deleted file mode 100644 index 86677fd7a..000000000 --- a/examples/protocols/http2_request/components/sh2lib/connectlib.h +++ /dev/null @@ -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_ */ diff --git a/examples/protocols/http2_request/components/sh2lib/sh2lib.c b/examples/protocols/http2_request/components/sh2lib/sh2lib.c index ba46593f6..04817e16f 100644 --- a/examples/protocols/http2_request/components/sh2lib/sh2lib.c +++ b/examples/protocols/http2_request/components/sh2lib/sh2lib.c @@ -21,8 +21,8 @@ #include #include #include +#include -#include "connectlib.h" #include "sh2lib.h" static const char *TAG = "sh2lib"; @@ -75,10 +75,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, (const char *)data, (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 == -SSL_ERROR_WANT_WRITE || rv == -SSL_ERROR_WANT_READ) { rv = NGHTTP2_ERR_WOULDBLOCK; } else { rv = NGHTTP2_ERR_CALLBACK_FAILURE; @@ -128,10 +127,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 == -SSL_ERROR_WANT_WRITE || rv == -SSL_ERROR_WANT_READ) { rv = NGHTTP2_ERR_WOULDBLOCK; } else { rv = NGHTTP2_ERR_CALLBACK_FAILURE; @@ -281,24 +279,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 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); + struct esp_tls_cfg tls_cfg; + tls_cfg.alpn_protos = (unsigned char *) "\x02h2"; + if ((hd->http2_tls = esp_tls_conn_http_new(uri, &tls_cfg)) == NULL) { + ESP_LOGE(TAG, "[sh2-connect] esp-tls connection failed"); goto error; } @@ -308,6 +292,9 @@ 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); @@ -320,17 +307,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 +325,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; } diff --git a/examples/protocols/http2_request/components/sh2lib/sh2lib.h b/examples/protocols/http2_request/components/sh2lib/sh2lib.h index 97095eb5a..0112768bb 100644 --- a/examples/protocols/http2_request/components/sh2lib/sh2lib.h +++ b/examples/protocols/http2_request/components/sh2lib/sh2lib.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 */ From 8a1dcc076577a101a3cda266ea4b61d113a6b687 Mon Sep 17 00:00:00 2001 From: Jitin George Date: Mon, 12 Feb 2018 23:38:51 +0530 Subject: [PATCH 10/17] CA Certificate verification --- components/esp-tls/esp-tls.c | 21 +++++++++++++++++++++ components/esp-tls/esp-tls.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp-tls.c index d7599ed53..e32792175 100644 --- a/components/esp-tls/esp-tls.c +++ b/components/esp-tls/esp-tls.c @@ -117,6 +117,27 @@ static int create_ssl_handle(struct esp_tls *tls, const char *hostname, size_t h SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); #endif + + if (cfg->cacert_pem_buf != NULL) { + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + + BIO *bio; + bio = BIO_new(BIO_s_mem()); + BIO_write(bio, cfg->cacert_pem_buf, cfg->cacert_pem_bytes); + + X509 *ca = PEM_read_bio_X509(bio, NULL, 0, NULL); + + if (!ca) { + ESP_LOGE(TAG, "CA Error\n"); + } + ESP_LOGD(TAG, "CA OK\n"); + + X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), ca); + + X509_free(ca); + BIO_free(bio); + } + if (cfg->alpn_protos) { SSL_CTX_set_alpn_protos(ssl_ctx, cfg->alpn_protos, strlen((char *)cfg->alpn_protos)); } diff --git a/components/esp-tls/esp-tls.h b/components/esp-tls/esp-tls.h index f921a5e6f..0327acc70 100644 --- a/components/esp-tls/esp-tls.h +++ b/components/esp-tls/esp-tls.h @@ -19,6 +19,8 @@ struct esp_tls_cfg { * - 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 { From 8cd3c479562ba31578f28ca54adbea3a7dd6fc39 Mon Sep 17 00:00:00 2001 From: Jitin George Date: Mon, 12 Feb 2018 23:41:31 +0530 Subject: [PATCH 11/17] esp-tls api support in https_request --- .../main/https_request_example_main.c | 160 +++--------------- 1 file changed, 28 insertions(+), 132 deletions(-) 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 93aaba74f..1faab9432 100644 --- a/examples/protocols/https_request/main/https_request_example_main.c +++ b/examples/protocols/https_request/main/https_request_example_main.c @@ -47,6 +47,8 @@ #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 +90,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 +135,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 +144,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); - goto exit; + struct esp_tls_cfg 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..."); + abort(); } - - 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, + (const 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); + } 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)); @@ -278,9 +178,9 @@ 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 == SSL_ERROR_WANT_WRITE || -ret == SSL_ERROR_WANT_READ) continue; if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { @@ -290,7 +190,7 @@ static void https_get_task(void *pvParameters) 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 +208,14 @@ 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) + 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; From e29294b49a587f0b8e2e9eac9e83863b6ae660cf Mon Sep 17 00:00:00 2001 From: Jitin George Date: Wed, 14 Feb 2018 15:15:50 +0530 Subject: [PATCH 12/17] Resolved Issues --- components/esp-tls/esp-tls.c | 71 +++++--- components/esp-tls/esp-tls.h | 161 ++++++++++++++---- docs/Doxyfile | 3 + docs/api-reference/protocols/esp_tls.rst | 24 +++ docs/en/api-reference/protocols/index.rst | 2 +- .../http2_request/components/sh2lib/sh2lib.c | 12 +- .../http2_request/components/sh2lib/sh2lib.h | 2 +- examples/protocols/https_request/README.md | 2 +- .../main/https_request_example_main.c | 34 +--- 9 files changed, 220 insertions(+), 91 deletions(-) create mode 100644 docs/api-reference/protocols/esp_tls.rst diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp-tls.c index e32792175..b56dd2db3 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 0327acc70..182034278 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; -}; +/** + * @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; -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); -}; - -/* - * - * 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. +/** + * @brief ESP-TLS Connection Handle */ -struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, struct esp_tls_cfg *cfg); +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; -/* 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 + * + * 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, 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 9e9f37780..e0c1e0d11 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 000000000..1552d7dab --- /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 b5572d8cd..fb9ca36a7 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 04817e16f..750ec45a2 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 0112768bb..b69c36739 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 fb7c6df95..7b158b6af 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 1faab9432..9dca5b61a 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 From 30b50cbfb3d54000f8568783fdb74eac48607d94 Mon Sep 17 00:00:00 2001 From: Jitin George Date: Wed, 14 Feb 2018 18:51:26 +0530 Subject: [PATCH 13/17] esp-tls header file name change --- components/esp-tls/{esp-tls.c => esp_tls.c} | 2 +- components/esp-tls/{esp-tls.h => esp_tls.h} | 0 examples/protocols/http2_request/components/sh2lib/sh2lib.c | 1 - examples/protocols/http2_request/components/sh2lib/sh2lib.h | 2 +- .../protocols/https_request/main/https_request_example_main.c | 2 +- 5 files changed, 3 insertions(+), 4 deletions(-) rename components/esp-tls/{esp-tls.c => esp_tls.c} (99%) rename components/esp-tls/{esp-tls.h => esp_tls.h} (100%) diff --git a/components/esp-tls/esp-tls.c b/components/esp-tls/esp_tls.c similarity index 99% rename from components/esp-tls/esp-tls.c rename to components/esp-tls/esp_tls.c index b56dd2db3..d6e79f54d 100644 --- a/components/esp-tls/esp-tls.c +++ b/components/esp-tls/esp_tls.c @@ -21,7 +21,7 @@ #include #include -#include "esp-tls.h" +#include "esp_tls.h" static const char *TAG = "esp-tls"; diff --git a/components/esp-tls/esp-tls.h b/components/esp-tls/esp_tls.h similarity index 100% rename from components/esp-tls/esp-tls.h rename to components/esp-tls/esp_tls.h diff --git a/examples/protocols/http2_request/components/sh2lib/sh2lib.c b/examples/protocols/http2_request/components/sh2lib/sh2lib.c index 750ec45a2..5a25f9bc4 100644 --- a/examples/protocols/http2_request/components/sh2lib/sh2lib.c +++ b/examples/protocols/http2_request/components/sh2lib/sh2lib.c @@ -20,7 +20,6 @@ #include #include #include -#include #include "sh2lib.h" diff --git a/examples/protocols/http2_request/components/sh2lib/sh2lib.h b/examples/protocols/http2_request/components/sh2lib/sh2lib.h index b69c36739..44d1a5333 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 "esp-tls.h" +#include "esp_tls.h" #include /* 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 9dca5b61a..51701a47c 100644 --- a/examples/protocols/https_request/main/https_request_example_main.c +++ b/examples/protocols/https_request/main/https_request_example_main.c @@ -38,7 +38,7 @@ #include "lwip/netdb.h" #include "lwip/dns.h" -#include "esp-tls.h" +#include "esp_tls.h" /* The examples use simple WiFi configuration that you can set via 'make menuconfig'. From 9c8a5ca9797cf046011f403b590c6a78a3762aa9 Mon Sep 17 00:00:00 2001 From: Jitin George Date: Mon, 26 Feb 2018 16:04:47 +0530 Subject: [PATCH 14/17] https mbedtls example --- examples/protocols/https_mbedtls/Makefile | 9 + .../https_mbedtls/main/Kconfig.projbuild | 17 + .../protocols/https_mbedtls/main/component.mk | 10 + .../main/https_mbedtls_example_main.c | 341 ++++++++++++++++++ .../https_mbedtls/main/server_root_cert.pem | 27 ++ .../main/https_request_example_main.c | 2 +- 6 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 examples/protocols/https_mbedtls/Makefile create mode 100644 examples/protocols/https_mbedtls/main/Kconfig.projbuild create mode 100644 examples/protocols/https_mbedtls/main/component.mk create mode 100644 examples/protocols/https_mbedtls/main/https_mbedtls_example_main.c create mode 100644 examples/protocols/https_mbedtls/main/server_root_cert.pem diff --git a/examples/protocols/https_mbedtls/Makefile b/examples/protocols/https_mbedtls/Makefile new file mode 100644 index 000000000..070f89048 --- /dev/null +++ b/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 + diff --git a/examples/protocols/https_mbedtls/main/Kconfig.projbuild b/examples/protocols/https_mbedtls/main/Kconfig.projbuild new file mode 100644 index 000000000..1c7241da3 --- /dev/null +++ b/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 diff --git a/examples/protocols/https_mbedtls/main/component.mk b/examples/protocols/https_mbedtls/main/component.mk new file mode 100644 index 000000000..818e2a182 --- /dev/null +++ b/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 + + diff --git a/examples/protocols/https_mbedtls/main/https_mbedtls_example_main.c b/examples/protocols/https_mbedtls/main/https_mbedtls_example_main.c new file mode 100644 index 000000000..93aaba74f --- /dev/null +++ b/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 +#include +#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 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); +} diff --git a/examples/protocols/https_mbedtls/main/server_root_cert.pem b/examples/protocols/https_mbedtls/main/server_root_cert.pem new file mode 100644 index 000000000..0002462ce --- /dev/null +++ b/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----- 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 51701a47c..479399a0a 100644 --- a/examples/protocols/https_request/main/https_request_example_main.c +++ b/examples/protocols/https_request/main/https_request_example_main.c @@ -146,7 +146,7 @@ static void https_get_task(void *pvParameters) ESP_LOGI(TAG, "Connection established..."); } else { ESP_LOGE(TAG, "Connection failed..."); - abort(); + goto exit; } size_t written_bytes = 0; From b09c3e98784b568353aca61a4c0be4866a901532 Mon Sep 17 00:00:00 2001 From: Jitin George Date: Wed, 28 Feb 2018 18:09:43 +0530 Subject: [PATCH 15/17] mbedtls integration in esp-tls --- components/esp-tls/esp_tls.c | 200 ++++++++++-------- components/esp-tls/esp_tls.h | 44 ++-- .../http2_request/components/sh2lib/sh2lib.c | 4 +- .../main/https_request_example_main.c | 8 +- 4 files changed, 152 insertions(+), 104 deletions(-) diff --git a/components/esp-tls/esp_tls.c b/components/esp-tls/esp_tls.c index d6e79f54d..d84cf77bf 100644 --- a/components/esp-tls/esp_tls.c +++ b/components/esp-tls/esp_tls.c @@ -23,7 +23,6 @@ #include #include "esp_tls.h" - static const char *TAG = "esp-tls"; #ifdef ESP_PLATFORM @@ -63,13 +62,14 @@ static ssize_t tcp_read(esp_tls_t *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); + ssize_t ret = mbedtls_ssl_read(&tls->ssl, (unsigned char *)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:", ret); - } - return -err; + 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; } @@ -89,17 +89,17 @@ static int esp_tcp_connect(const char *host, int hostlen, int port) 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; + 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; + 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; + goto err_freesocket; } ret = connect(fd, addr_ptr, res->ai_addrlen); @@ -117,74 +117,112 @@ err_freeaddr: 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; - 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; + 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; } -#ifdef __APPLE__ - SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); -#endif - - if (cfg->cacert_pem_buf != NULL) { - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - - BIO *bio; - bio = BIO_new(BIO_s_mem()); - BIO_write(bio, cfg->cacert_pem_buf, cfg->cacert_pem_bytes); - - X509 *ca = PEM_read_bio_X509(bio, NULL, 0, NULL); - - if (!ca) { - ESP_LOGE(TAG, "CA Error"); - X509_free(ca); - BIO_free(bio); - SSL_CTX_free(ssl_ctx); - return -1; - } - ESP_LOGD(TAG, "CA OK"); - - X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), ca); - - X509_free(ca); - BIO_free(bio); - } - - if (cfg->alpn_protos) { - SSL_CTX_set_alpn_protos(ssl_ctx, cfg->alpn_protos, strlen((char *)cfg->alpn_protos)); - } - SSL *ssl = SSL_new(ssl_ctx); - if (!ssl) { - SSL_CTX_free(ssl_ctx); - return -1; - } - + + /* Hostname set here should match CN in server certificate */ char *use_host = strndup(hostname, hostlen); if (!use_host) { - SSL_CTX_free(ssl_ctx); - return -1; + 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; } - SSL_set_tlsext_host_name(ssl, use_host); free(use_host); - SSL_set_fd(ssl, tls->sockfd); - ret = SSL_connect(ssl); - if (ret < 1) { - ESP_LOGE(TAG, "SSL handshake failed"); - SSL_free(ssl); - SSL_CTX_free(ssl_ctx); - return -1; + 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; } - tls->ctx = ssl_ctx; - tls->ssl = ssl; + 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; } /** @@ -192,15 +230,7 @@ static int create_ssl_handle(esp_tls_t *tls, const char *hostname, size_t hostle */ void esp_tls_conn_delete(esp_tls_t *tls) { - if (!tls) { - return; - } - if (tls->ssl) { - SSL_free(tls->ssl); - } - if (tls->ctx) { - SSL_CTX_free(tls->ctx); - } + mbedtls_cleanup(tls); if (tls->sockfd) { close(tls->sockfd); } @@ -214,13 +244,11 @@ static ssize_t tcp_write(esp_tls_t *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); + ssize_t ret = mbedtls_ssl_write(&tls->ssl, (unsigned char*) 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:", ret); - } - return -err; + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + ESP_LOGE(TAG, "write error :%d:", ret); + } } return ret; } @@ -253,7 +281,7 @@ esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const e tls->write = tls_write; } - if(cfg->non_block == true) { + if (cfg->non_block == true) { int flags = fcntl(tls->sockfd, F_GETFL, 0); fcntl(tls->sockfd, F_SETFL, flags | O_NONBLOCK); } diff --git a/components/esp-tls/esp_tls.h b/components/esp-tls/esp_tls.h index 182034278..4f2f35a03 100644 --- a/components/esp-tls/esp_tls.h +++ b/components/esp-tls/esp_tls.h @@ -16,9 +16,18 @@ #include #include -#include #include + +#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 @@ -36,13 +45,12 @@ typedef struct esp_tls_cfg { "\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 */ @@ -52,15 +60,27 @@ typedef struct esp_tls_cfg { * @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. */ + 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; diff --git a/examples/protocols/http2_request/components/sh2lib/sh2lib.c b/examples/protocols/http2_request/components/sh2lib/sh2lib.c index 5a25f9bc4..c5bae587b 100644 --- a/examples/protocols/http2_request/components/sh2lib/sh2lib.c +++ b/examples/protocols/http2_request/components/sh2lib/sh2lib.c @@ -75,7 +75,7 @@ static ssize_t callback_send_inner(struct sh2lib_handle *hd, const uint8_t *data { int rv = esp_tls_conn_write(hd->http2_tls, data, length); if (rv <= 0) { - if (rv == -SSL_ERROR_WANT_WRITE || rv == -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; @@ -127,7 +127,7 @@ static ssize_t callback_recv(nghttp2_session *session, uint8_t *buf, int rv; rv = esp_tls_conn_read(hd->http2_tls, (char *)buf, (int)length); if (rv < 0) { - if (rv == -SSL_ERROR_WANT_WRITE || rv == -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; 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 479399a0a..3964b4ea5 100644 --- a/examples/protocols/https_request/main/https_request_example_main.c +++ b/examples/protocols/https_request/main/https_request_example_main.c @@ -157,7 +157,7 @@ static void https_get_task(void *pvParameters) 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) { + } 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; } @@ -171,12 +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 == MBEDTLS_ERR_SSL_WANT_WRITE || ret == MBEDTLS_ERR_SSL_WANT_READ) continue; 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; } From 254212b02b5210ee8289276d5a0c26208501eb51 Mon Sep 17 00:00:00 2001 From: Jitin George Date: Mon, 19 Mar 2018 20:01:12 +0530 Subject: [PATCH 16/17] changes to accomodate new Doc structure --- docs/{ => en}/api-reference/protocols/esp_tls.rst | 0 docs/zh_CN/api-reference/protocols/esp_tls.rst | 1 + 2 files changed, 1 insertion(+) rename docs/{ => en}/api-reference/protocols/esp_tls.rst (100%) create mode 100644 docs/zh_CN/api-reference/protocols/esp_tls.rst diff --git a/docs/api-reference/protocols/esp_tls.rst b/docs/en/api-reference/protocols/esp_tls.rst similarity index 100% rename from docs/api-reference/protocols/esp_tls.rst rename to docs/en/api-reference/protocols/esp_tls.rst diff --git a/docs/zh_CN/api-reference/protocols/esp_tls.rst b/docs/zh_CN/api-reference/protocols/esp_tls.rst new file mode 100644 index 000000000..d15c745d8 --- /dev/null +++ b/docs/zh_CN/api-reference/protocols/esp_tls.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/protocols/esp_tls.rst From 9d416fe84e1c315df2762e7d8e523c8e22be04cd Mon Sep 17 00:00:00 2001 From: Jitin George Date: Fri, 6 Apr 2018 18:16:40 +0530 Subject: [PATCH 17/17] resolve merge conflicts --- .../http2_request/components/sh2lib/sh2lib.c | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/examples/protocols/http2_request/components/sh2lib/sh2lib.c b/examples/protocols/http2_request/components/sh2lib/sh2lib.c index c5bae587b..3abfc5c5c 100644 --- a/examples/protocols/http2_request/components/sh2lib/sh2lib.c +++ b/examples/protocols/http2_request/components/sh2lib/sh2lib.c @@ -27,43 +27,6 @@ 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