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

feature/add simplified TLS socket APIs

See merge request idf/esp-idf!1951
This commit is contained in:
Ivan Grokhotkov 2018-04-09 14:25:21 +08:00
commit b11209b070
18 changed files with 972 additions and 431 deletions

View file

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

View file

@ -0,0 +1,319 @@
// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <http_parser.h>
#include "esp_tls.h"
static const char *TAG = "esp-tls";
#ifdef ESP_PLATFORM
#include <esp_log.h>
#else
#define ESP_LOGD(TAG, ...) //printf(__VA_ARGS__);
#define ESP_LOGE(TAG, ...) printf(__VA_ARGS__);
#endif
static struct addrinfo *resolve_host_name(const char *host, size_t hostlen)
{
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
char *use_host = strndup(host, hostlen);
if (!use_host) {
return NULL;
}
ESP_LOGD(TAG, "host:%s: strlen %lu", use_host, (unsigned long)hostlen);
struct addrinfo *res;
if (getaddrinfo(use_host, NULL, &hints, &res)) {
ESP_LOGE(TAG, "couldn't get hostname for :%s:", use_host);
free(use_host);
return NULL;
}
free(use_host);
return res;
}
static ssize_t tcp_read(esp_tls_t *tls, char *data, size_t datalen)
{
return recv(tls->sockfd, data, datalen, 0);
}
static ssize_t tls_read(esp_tls_t *tls, char *data, size_t datalen)
{
ssize_t ret = mbedtls_ssl_read(&tls->ssl, (unsigned char *)data, datalen);
if (ret < 0) {
if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
return 0;
}
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "read error :%d:", ret);
}
}
return ret;
}
static int esp_tcp_connect(const char *host, int hostlen, int port)
{
struct addrinfo *res = resolve_host_name(host, hostlen);
if (!res) {
return -1;
}
int ret = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (ret < 0) {
goto err_freeaddr;
}
int fd = ret;
void *addr_ptr;
if (res->ai_family == AF_INET) {
struct sockaddr_in *p = (struct sockaddr_in *)res->ai_addr;
p->sin_port = htons(port);
addr_ptr = p;
} else if (res->ai_family == AF_INET6) {
struct sockaddr_in6 *p = (struct sockaddr_in6 *)res->ai_addr;
p->sin6_port = htons(port);
p->sin6_family = AF_INET6;
addr_ptr = p;
} else {
/* Unsupported Protocol Family */
goto err_freesocket;
}
ret = connect(fd, addr_ptr, res->ai_addrlen);
if (ret < 0) {
goto err_freesocket;
}
freeaddrinfo(res);
return fd;
err_freesocket:
close(fd);
err_freeaddr:
freeaddrinfo(res);
return -1;
}
static void verify_certificate(esp_tls_t *tls)
{
int flags;
char buf[100];
if ((flags = mbedtls_ssl_get_verify_result(&tls->ssl)) != 0) {
ESP_LOGI(TAG, "Failed to verify peer certificate!");
bzero(buf, sizeof(buf));
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags);
ESP_LOGI(TAG, "verification info: %s", buf);
} else {
ESP_LOGI(TAG, "Certificate verified.");
}
}
static void mbedtls_cleanup(esp_tls_t *tls)
{
if (!tls) {
return;
}
mbedtls_entropy_free(&tls->entropy);
mbedtls_ssl_config_free(&tls->conf);
mbedtls_ctr_drbg_free(&tls->ctr_drbg);
mbedtls_ssl_free(&tls->ssl);
mbedtls_net_free(&tls->server_fd);
}
static int create_ssl_handle(esp_tls_t *tls, const char *hostname, size_t hostlen, const esp_tls_cfg_t *cfg)
{
int ret;
mbedtls_net_init(&tls->server_fd);
tls->server_fd.fd = tls->sockfd;
mbedtls_ssl_init(&tls->ssl);
mbedtls_ctr_drbg_init(&tls->ctr_drbg);
mbedtls_ssl_config_init(&tls->conf);
mbedtls_entropy_init(&tls->entropy);
if ((ret = mbedtls_ctr_drbg_seed(&tls->ctr_drbg,
mbedtls_entropy_func, &tls->entropy, NULL, 0)) != 0) {
ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret);
goto exit;
}
/* Hostname set here should match CN in server certificate */
char *use_host = strndup(hostname, hostlen);
if (!use_host) {
goto exit;
}
if ((ret = mbedtls_ssl_set_hostname(&tls->ssl, use_host)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret);
free(use_host);
goto exit;
}
free(use_host);
if ((ret = mbedtls_ssl_config_defaults(&tls->conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret);
goto exit;
}
if (cfg->cacert_pem_buf != NULL) {
mbedtls_x509_crt_init(&tls->cacert);
ret = mbedtls_x509_crt_parse(&tls->cacert, cfg->cacert_pem_buf, cfg->cacert_pem_bytes);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
goto exit;
}
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->cacert, NULL);
} else {
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
}
mbedtls_ssl_conf_rng(&tls->conf, mbedtls_ctr_drbg_random, &tls->ctr_drbg);
#ifdef CONFIG_MBEDTLS_DEBUG
mbedtls_esp_enable_debug_log(&tls->conf, 4);
#endif
if ((ret = mbedtls_ssl_setup(&tls->ssl, &tls->conf)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x\n\n", -ret);
goto exit;
}
mbedtls_ssl_set_bio(&tls->ssl, &tls->server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
while ((ret = mbedtls_ssl_handshake(&tls->ssl)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "mbedtls_ssl_handshake returned -0x%x", -ret);
if (cfg->cacert_pem_buf != NULL) {
/* This is to check whether handshake failed due to invalid certificate*/
verify_certificate(tls);
}
goto exit;
}
}
return 0;
exit:
mbedtls_cleanup(tls);
return -1;
}
/**
* @brief Close the TLS connection and free any allocated resources.
*/
void esp_tls_conn_delete(esp_tls_t *tls)
{
mbedtls_cleanup(tls);
if (tls->sockfd) {
close(tls->sockfd);
}
free(tls);
};
static ssize_t tcp_write(esp_tls_t *tls, const char *data, size_t datalen)
{
return send(tls->sockfd, data, datalen, 0);
}
static ssize_t tls_write(esp_tls_t *tls, const char *data, size_t datalen)
{
ssize_t ret = mbedtls_ssl_write(&tls->ssl, (unsigned char*) data, datalen);
if (ret < 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "write error :%d:", ret);
}
}
return ret;
}
/**
* @brief Create a new TLS/SSL connection
*/
esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg)
{
int sockfd = esp_tcp_connect(hostname, hostlen, port);
if (sockfd < 0) {
return NULL;
}
esp_tls_t *tls = (esp_tls_t *)calloc(1, sizeof(esp_tls_t));
if (!tls) {
close(sockfd);
return NULL;
}
tls->sockfd = sockfd;
tls->read = tcp_read;
tls->write = tcp_write;
if (cfg) {
if (create_ssl_handle(tls, hostname, hostlen, cfg) != 0) {
esp_tls_conn_delete(tls);
return NULL;
}
tls->read = tls_read;
tls->write = tls_write;
}
if (cfg->non_block == true) {
int flags = fcntl(tls->sockfd, F_GETFL, 0);
fcntl(tls->sockfd, F_SETFL, flags | O_NONBLOCK);
}
return tls;
}
static int get_port(const char *url, struct http_parser_url *u)
{
if (u->field_data[UF_PORT].len) {
return strtol(&url[u->field_data[UF_PORT].off], NULL, 10);
} else {
if (strncmp(&url[u->field_data[UF_SCHEMA].off], "http", u->field_data[UF_SCHEMA].len) == 0) {
return 80;
} else if (strncmp(&url[u->field_data[UF_SCHEMA].off], "https", u->field_data[UF_SCHEMA].len) == 0) {
return 443;
}
}
return 0;
}
/**
* @brief Create a new TLS/SSL connection with a given "HTTP" url
*/
esp_tls_t *esp_tls_conn_http_new(const char *url, const esp_tls_cfg_t *cfg)
{
/* Parse URI */
struct http_parser_url u;
http_parser_url_init(&u);
http_parser_parse_url(url, strlen(url), 0, &u);
/* Connect to host */
return esp_tls_conn_new(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len,
get_port(url, &u), cfg);
}

View file

@ -0,0 +1,172 @@
// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_TLS_H_
#define _ESP_TLS_H_
#include <stdbool.h>
#include <sys/socket.h>
#include <fcntl.h>
#include "mbedtls/platform.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/esp_debug.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
#include "mbedtls/certs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ESP-TLS configuration parameters
*/
typedef struct esp_tls_cfg {
const unsigned char *alpn_protos; /*!< Application protocols required for HTTP2.
If HTTP2/ALPN support is required, a list
of protocols that should be negotiated.
The format is length followed by protocol
name.
For the most common cases the following is ok:
"\x02h2"
- where the first '2' is the length of the protocol and
- the subsequent 'h2' is the protocol name */
const unsigned char *cacert_pem_buf; /*!< Certificate Authority's certificate in a buffer */
const unsigned int cacert_pem_bytes; /*!< Size of Certificate Authority certificate
pointed to by cacert_pem_buf */
bool non_block; /*!< Configure non-blocking mode. If set to true the
underneath socket will be configured in non
blocking mode after tls session is established */
} esp_tls_cfg_t;
/**
* @brief ESP-TLS Connection Handle
*/
typedef struct esp_tls {
mbedtls_ssl_context ssl; /*!< TLS/SSL context */
mbedtls_entropy_context entropy; /*!< mbedTLS entropy context structure */
mbedtls_ctr_drbg_context ctr_drbg; /*!< mbedTLS ctr drbg context structure.
CTR_DRBG is deterministic random
bit generation based on AES-256 */
mbedtls_ssl_config conf; /*!< TLS/SSL configuration to be shared
between mbedtls_ssl_context
structures */
mbedtls_net_context server_fd; /*!< mbedTLS wrapper type for sockets */
mbedtls_x509_crt cacert; /*!< Container for an X.509 certificate */
int sockfd; /*!< Underlying socket file descriptor. */
ssize_t (*read)(struct esp_tls *tls, char *data, size_t datalen); /*!< Callback function for reading data from TLS/SSL
connection. */
ssize_t (*write)(struct esp_tls *tls, const char *data, size_t datalen); /*!< Callback function for writing data to TLS/SSL
connection. */
} esp_tls_t;
/**
* @brief Create a new TLS/SSL connection
*
* This function establishes a TLS/SSL connection with the specified host.
*
* @param[in] hostname Hostname of the host.
* @param[in] hostlen Length of hostname.
* @param[in] port Port number of the host.
* @param[in] cfg TLS configuration as esp_tls_cfg_t. If you wish to open
* non-TLS connection, keep this NULL. For TLS connection,
* a pass pointer to esp_tls_cfg_t. At a minimum, this
* structure should be zero-initialized.
* @return pointer to esp_tls_t, or NULL if connection couldn't be opened.
*/
esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg);
/**
* @brief Create a new TLS/SSL connection with a given "HTTP" url
*
* The behaviour is same as esp_tls_conn_new() API. However this API accepts host's url.
*
* @param[in] url url of host.
* @param[in] cfg TLS configuration as esp_tls_cfg_t. If you wish to open
* non-TLS connection, keep this NULL. For TLS connection,
* a pass pointer to 'esp_tls_cfg_t'. At a minimum, this
* structure should be zero-initialized.
* @return pointer to esp_tls_t, or NULL if connection couldn't be opened.
*/
esp_tls_t *esp_tls_conn_http_new(const char *url, const esp_tls_cfg_t *cfg);
/**
* @brief Write from buffer 'data' into specified tls connection.
*
* @param[in] tls pointer to esp-tls as esp-tls handle.
* @param[in] data Buffer from which data will be written.
* @param[in] datalen Length of data buffer.
*
* @return
* - >0 if write operation was successful, the return value is the number
* of bytes actually written to the TLS/SSL connection.
* - 0 if write operation was not successful. The underlying
* connection was closed.
* - <0 if write operation was not successful, because either an
* error occured or an action must be taken by the calling process.
*/
static inline ssize_t esp_tls_conn_write(esp_tls_t *tls, const void *data, size_t datalen)
{
return tls->write(tls, (char *)data, datalen);
}
/**
* @brief Read from specified tls connection into the buffer 'data'.
*
* @param[in] tls pointer to esp-tls as esp-tls handle.
* @param[in] data Buffer to hold read data.
* @param[in] datalen Length of data buffer.
*
* @return
* - >0 if read operation was successful, the return value is the number
* of bytes actually read from the TLS/SSL connection.
* - 0 if read operation was not successful. The underlying
* connection was closed.
* - <0 if read operation was not successful, because either an
* error occured or an action must be taken by the calling process.
*/
static inline ssize_t esp_tls_conn_read(esp_tls_t *tls, void *data, size_t datalen)
{
return tls->read(tls, (char *)data, datalen);
}
/**
* @brief Close the TLS/SSL connection and free any allocated resources.
*
* This function should be called to close each tls connection opened with esp_tls_conn_new() or
* esp_tls_conn_http_new() APIs.
*
* @param[in] tls pointer to esp-tls as esp-tls handle.
*/
void esp_tls_conn_delete(esp_tls_t *tls);
#ifdef __cplusplus
}
#endif
#endif /* ! _ESP_TLS_H_ */

View file

@ -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

View file

@ -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

View file

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

View file

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

View file

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

View file

@ -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_ */

View file

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

View file

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

View file

@ -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

View file

@ -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

View file

@ -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

View file

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

View file

@ -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-----

View file

@ -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.

View file

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