From 5478203fd2f3df31c9c34b69b08ef71883d50792 Mon Sep 17 00:00:00 2001 From: Jitin George Date: Thu, 20 Dec 2018 12:31:49 +0530 Subject: [PATCH] example/https_request: Add example with esp-tls API usage --- .../protocols/https_request/CMakeLists.txt | 6 + examples/protocols/https_request/Makefile | 9 + examples/protocols/https_request/README.md | 5 + .../https_request/main/CMakeLists.txt | 10 + .../https_request/main/Kconfig.projbuild | 17 ++ .../protocols/https_request/main/component.mk | 10 + .../main/https_request_example_main.c | 217 ++++++++++++++++++ .../https_request/main/server_root_cert.pem | 27 +++ .../https_request/sdkconfig.defaults | 2 + 9 files changed, 303 insertions(+) create mode 100644 examples/protocols/https_request/CMakeLists.txt create mode 100644 examples/protocols/https_request/Makefile create mode 100644 examples/protocols/https_request/README.md create mode 100644 examples/protocols/https_request/main/CMakeLists.txt create mode 100644 examples/protocols/https_request/main/Kconfig.projbuild create mode 100644 examples/protocols/https_request/main/component.mk create mode 100644 examples/protocols/https_request/main/https_request_example_main.c create mode 100644 examples/protocols/https_request/main/server_root_cert.pem create mode 100644 examples/protocols/https_request/sdkconfig.defaults diff --git a/examples/protocols/https_request/CMakeLists.txt b/examples/protocols/https_request/CMakeLists.txt new file mode 100644 index 00000000..f77bc661 --- /dev/null +++ b/examples/protocols/https_request/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(https_request) diff --git a/examples/protocols/https_request/Makefile b/examples/protocols/https_request/Makefile new file mode 100644 index 00000000..55c5f943 --- /dev/null +++ b/examples/protocols/https_request/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_request + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/protocols/https_request/README.md b/examples/protocols/https_request/README.md new file mode 100644 index 00000000..7b158b6a --- /dev/null +++ b/examples/protocols/https_request/README.md @@ -0,0 +1,5 @@ +# HTTPS Request Example + +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/CMakeLists.txt b/examples/protocols/https_request/main/CMakeLists.txt new file mode 100644 index 00000000..60a1a066 --- /dev/null +++ b/examples/protocols/https_request/main/CMakeLists.txt @@ -0,0 +1,10 @@ +set(COMPONENT_SRCS "https_request_example_main.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + + +# Embed the server root certificate into the final binary +# +# (If this was a component, we would set COMPONENT_EMBED_TXTFILES here.) +set(COMPONENT_EMBED_TXTFILES server_root_cert.pem) + +register_component() diff --git a/examples/protocols/https_request/main/Kconfig.projbuild b/examples/protocols/https_request/main/Kconfig.projbuild new file mode 100644 index 00000000..1c7241da --- /dev/null +++ b/examples/protocols/https_request/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_request/main/component.mk b/examples/protocols/https_request/main/component.mk new file mode 100644 index 00000000..818e2a18 --- /dev/null +++ b/examples/protocols/https_request/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_request/main/https_request_example_main.c b/examples/protocols/https_request/main/https_request_example_main.c new file mode 100644 index 00000000..4450a8fb --- /dev/null +++ b/examples/protocols/https_request/main/https_request_example_main.c @@ -0,0 +1,217 @@ +/* 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 "esp_tls.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, len; + + 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"); + 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_new(WEB_SERVER, strlen(WEB_SERVER), WEB_PORT, &cfg); + + if(tls != NULL) { + ESP_LOGI(TAG, "Connection established..."); + } else { + ESP_LOGE(TAG, "Connection failed..."); + goto exit; + } + + size_t written_bytes = 0; + do { + 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_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)); + + ESP_LOGI(TAG, "Reading HTTP response..."); + + do + { + len = sizeof(buf) - 1; + bzero(buf, sizeof(buf)); + 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 < 0) + { + ESP_LOGE(TAG, "esp_tls_conn_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); + + exit: + esp_tls_conn_delete(tls); + 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_request/main/server_root_cert.pem b/examples/protocols/https_request/main/server_root_cert.pem new file mode 100644 index 00000000..0002462c --- /dev/null +++ b/examples/protocols/https_request/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/sdkconfig.defaults b/examples/protocols/https_request/sdkconfig.defaults new file mode 100644 index 00000000..1bf47081 --- /dev/null +++ b/examples/protocols/https_request/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_SSL_USING_MBEDTLS=y +CONFIG_LWIP_IPV6=y