From cddb8c29e6923708b98739a0004d1758889df213 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Sat, 26 Dec 2020 12:43:12 +0530 Subject: [PATCH 1/5] esp-tls: Fix mem leak when global_ca_store is freed --- components/esp-tls/esp_tls_mbedtls.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/esp-tls/esp_tls_mbedtls.c b/components/esp-tls/esp_tls_mbedtls.c index e89e84d6f0..7dc32428de 100644 --- a/components/esp-tls/esp_tls_mbedtls.c +++ b/components/esp-tls/esp_tls_mbedtls.c @@ -639,6 +639,7 @@ esp_err_t esp_mbedtls_set_global_ca_store(const unsigned char *cacert_pem_buf, c if (ret < 0) { ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x", -ret); mbedtls_x509_crt_free(global_cacert); + free(global_cacert); global_cacert = NULL; return ESP_FAIL; } else if (ret > 0) { @@ -657,6 +658,7 @@ void esp_mbedtls_free_global_ca_store(void) { if (global_cacert) { mbedtls_x509_crt_free(global_cacert); + free(global_cacert); global_cacert = NULL; } } From 1a09e16af282807d20119cbaf9ae0aaa8a8ba6fb Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Thu, 7 Jan 2021 16:36:04 +0530 Subject: [PATCH 2/5] esp_tls: Fix memory leak when esp-tls server session is deleted --- components/esp-tls/esp_tls_mbedtls.c | 1 + components/esp-tls/esp_tls_wolfssl.c | 1 + 2 files changed, 2 insertions(+) diff --git a/components/esp-tls/esp_tls_mbedtls.c b/components/esp-tls/esp_tls_mbedtls.c index 7dc32428de..2be611876d 100644 --- a/components/esp-tls/esp_tls_mbedtls.c +++ b/components/esp-tls/esp_tls_mbedtls.c @@ -604,6 +604,7 @@ void esp_mbedtls_server_session_delete(esp_tls_t *tls) { if (tls != NULL) { esp_mbedtls_cleanup(tls); + esp_tls_internal_event_tracker_destroy(tls->error_handle); free(tls); } }; diff --git a/components/esp-tls/esp_tls_wolfssl.c b/components/esp-tls/esp_tls_wolfssl.c index 15ee7da3b0..6f71c81f9a 100644 --- a/components/esp-tls/esp_tls_wolfssl.c +++ b/components/esp-tls/esp_tls_wolfssl.c @@ -464,6 +464,7 @@ void esp_wolfssl_server_session_delete(esp_tls_t *tls) { if (tls != NULL) { esp_wolfssl_cleanup(tls); + esp_tls_internal_event_tracker_destroy(tls->error_handle); free(tls); } } From 43a1b53aad6cc694fa2c60a4bb8fee3108cbf269 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Mon, 11 Jan 2021 08:33:49 +0530 Subject: [PATCH 3/5] esp_tls_wolfssl: Move order of crt_bundle check to match that in esp_tls_mbedtls --- components/esp-tls/esp_tls_wolfssl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/esp-tls/esp_tls_wolfssl.c b/components/esp-tls/esp_tls_wolfssl.c index 6f71c81f9a..9faab69119 100644 --- a/components/esp-tls/esp_tls_wolfssl.c +++ b/components/esp-tls/esp_tls_wolfssl.c @@ -157,6 +157,11 @@ static esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls return ESP_ERR_WOLFSSL_CTX_SETUP_FAILED; } + if (cfg->crt_bundle_attach != NULL) { + ESP_LOGE(TAG,"use_crt_bundle not supported in wolfssl"); + return ESP_FAIL; + } + if (cfg->use_global_ca_store == true) { if ((esp_load_wolfssl_verify_buffer(tls, global_cacert, global_cacert_pem_bytes, FILE_TYPE_CA_CERT, &ret)) != ESP_OK) { ESP_LOGE(TAG, "Error in loading certificate verify buffer, returned %d", ret); @@ -223,11 +228,6 @@ static esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls return ESP_FAIL; } - if (cfg->crt_bundle_attach != NULL) { - ESP_LOGE(TAG,"use_crt_bundle not supported in wolfssl"); - return ESP_FAIL; - } - tls->priv_ssl =(void *)wolfSSL_new( (WOLFSSL_CTX *)tls->priv_ctx); if (!tls->priv_ssl) { ESP_LOGE(TAG, "Create wolfSSL failed"); From cc3843263a4ace7da0c1f31ef4c5c4bee505e936 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Wed, 2 Dec 2020 20:05:43 +0530 Subject: [PATCH 4/5] esp-tls: Added unit tests --- components/esp-tls/test/CMakeLists.txt | 2 + components/esp-tls/test/component.mk | 4 + components/esp-tls/test/test_esp_tls.c | 128 ++++++++++++++++++++++ tools/unit-test-app/configs/default_2 | 2 +- tools/unit-test-app/configs/default_2_c3 | 2 +- tools/unit-test-app/configs/default_2_s2 | 2 +- tools/unit-test-app/configs/esp_tls | 5 + tools/unit-test-app/configs/psram | 2 +- tools/unit-test-app/configs/release_2 | 2 +- tools/unit-test-app/configs/single_core_2 | 2 +- 10 files changed, 145 insertions(+), 6 deletions(-) create mode 100644 components/esp-tls/test/CMakeLists.txt create mode 100644 components/esp-tls/test/component.mk create mode 100644 components/esp-tls/test/test_esp_tls.c create mode 100644 tools/unit-test-app/configs/esp_tls diff --git a/components/esp-tls/test/CMakeLists.txt b/components/esp-tls/test/CMakeLists.txt new file mode 100644 index 0000000000..452d0b2c0e --- /dev/null +++ b/components/esp-tls/test/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRC_DIRS "." + PRIV_REQUIRES test_utils esp-tls) diff --git a/components/esp-tls/test/component.mk b/components/esp-tls/test/component.mk new file mode 100644 index 0000000000..8c6eb513e4 --- /dev/null +++ b/components/esp-tls/test/component.mk @@ -0,0 +1,4 @@ +# +#Component Makefile +# +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/esp-tls/test/test_esp_tls.c b/components/esp-tls/test/test_esp_tls.c new file mode 100644 index 0000000000..34ed120c17 --- /dev/null +++ b/components/esp-tls/test/test_esp_tls.c @@ -0,0 +1,128 @@ +// Copyright 2021 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 "test_utils.h" +#include "esp_tls.h" +#include "unity.h" +#include "esp_err.h" +#include "esp_log.h" +#if SOC_SHA_SUPPORT_PARALLEL_ENG +#include "sha/sha_parallel_engine.h" +#elif SOC_SHA_SUPPORT_DMA +#include "sha/sha_dma.h" +#endif + +const char *test_cert_pem = "-----BEGIN CERTIFICATE-----\n"\ + "MIICrDCCAZQCCQD88gCs5AFs/jANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1F\n"\ + "U1AtVExTIFRlc3RzMB4XDTIxMDEwNzAxMTc1OVoXDTMxMDEwNTAxMTc1OVowGDEW\n"\ + "MBQGA1UEAwwNRVNQLVRMUyBUZXN0czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\n"\ + "AQoCggEBAMn4KrV9M40PcxDNdmwfFMXI5cydrV3nm1o1QzcDDuYn1YfxoCCcaIc0\n"\ + "JgHox948u94z32vnJU5qXVgEvwkD3nWGzWqjkwB1NlnIuF10XQFLa8cIas7Db/hd\n"\ + "KygbIbYwhtwQAQFEt0/MiUbA+m+rjzvffV9pDcPRlCOE6pC+X3iC7Ft3aevkEBtR\n"\ + "EPPN0sxGx0iFRIsglpDcEKUONYjF46q90LDmjA4EbqvK7XBuXjZ1QRGcQ871/8Ht\n"\ + "LzHjZKc5IcIxBVoVNJo0sgHHBOaG7J8x2eAJX8DfK6GseGZDbuptiJyZoj4nmOXx\n"\ + "iToEPG08DPlMHy90TDHXZbVeXNHzFV8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA\n"\ + "n3oD/VONY9WIf0jvlAn62+VheABHFQqMrH02ucDxj0lRlgS4Z6+gRN2ITkzs8eNN\n"\ + "UglqVZCjU1mstL10tD8mwH8ShelxTWzHavc+GmAiEQx9C9zpMa7Oa+t6xFZCF6mi\n"\ + "c9h+3oNeql7zugcHjFXnlPd+oFhDTzH8R4Y+drZyR5LZsT5SNeEvLs7DEBMHNdKC\n"\ + "GOuWb7JNNhZKvNTymfCtM98s+7cbukwPogfWbaI+CjT49v4NOQlHNSN2Ldxpg9eh\n"\ + "fDgV8LqpUghnYjpe5lHgEqSZxV57z+amv5DRJNsH5+hB4k6HnRjfjQZqHUPlQ7Bk\n"\ + "pC4dhDzYsU8L1MN9e2i5Ig==\n"\ + "-----END CERTIFICATE-----\n"; + +const char *test_key_pem = "-----BEGIN PRIVATE KEY-----\n"\ + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJ+Cq1fTOND3MQ\n"\ + "zXZsHxTFyOXMna1d55taNUM3Aw7mJ9WH8aAgnGiHNCYB6MfePLveM99r5yVOal1Y\n"\ + "BL8JA951hs1qo5MAdTZZyLhddF0BS2vHCGrOw2/4XSsoGyG2MIbcEAEBRLdPzIlG\n"\ + "wPpvq487331faQ3D0ZQjhOqQvl94guxbd2nr5BAbURDzzdLMRsdIhUSLIJaQ3BCl\n"\ + "DjWIxeOqvdCw5owOBG6ryu1wbl42dUERnEPO9f/B7S8x42SnOSHCMQVaFTSaNLIB\n"\ + "xwTmhuyfMdngCV/A3yuhrHhmQ27qbYicmaI+J5jl8Yk6BDxtPAz5TB8vdEwx12W1\n"\ + "XlzR8xVfAgMBAAECggEAMPHNF9JRjduynN3N1wHpPM0ks2N6wgkAounJcilxkOES\n"\ + "ZDFwBrcgtbMzir7owLijJoQXnsSTS9+G42+zA/JGFOEoIrGlTDUbiMKVu0AZ0dh/\n"\ + "M+qcR6MhHWUmsj9w+f/kjqvqMm5+pf4BPn5BspI20eGlNlv1FAFUDw+E8LLR3Bgf\n"\ + "6Wl1BfuKHvdIRUd8nnJlDd0eeFap7yI/b2RDdvkOl4ounlKQhwpyJue2fbYJ2Z2v\n"\ + "pCIc1Nt3lZv4KE14nBRNH95nfakA+BmIos/pN78QRtSkSQN/9drDik46+t5Bz1Lm\n"\ + "kjFaujF73szV9mOx2O1JekziyfSJkVOJGqFSrzeIUQKBgQDvNpade0PlV+4Mh+Ra\n"\ + "p66OhsZGisl92GZZztK660pixzXoC6cAllxutBHSmu6GM9ISa5GoXMIgmD8d16p0\n"\ + "eXtBHuKysi/0iLkbfl1EtztB8vcChdzT0JCWD2vSqGq3772GT3TV1+2OZFsnpiON\n"\ + "3Jy0UbHAclMHZrBknYHEOkpTpQKBgQDYJIBeUya5y0MtvcR+hu+Hku8eJq9Tr8jx\n"\ + "Yuw5ihlg9rtsEhiWf8O0gnG9e5RrJiOAHcbDcAFx3AbB8ks8CpZ+7Cu9+Wq0VQND\n"\ + "ySJwuWXHZfgPEIvj5MlSuaaYP52k7tAF1HJhP5H8DyHMEkmUaPvk9cYRIQNsnADD\n"\ + "a3KDPgjlswKBgQCMTALYCCSRDRojpUvL046wLuF5HAsC0PlucLvtPvRYrKe0Galv\n"\ + "0cASeW/+ijmc4iWyAf7itMxyW3ihIwzo1Rrx3ZBt583yaIHQWzOOirypeDtg8mvK\n"\ + "mwdBy3ecjIRSrrwEWvOWO9cq7Tb9MRanLB2IMGXFDggeTh7kU/B4ywsutQKBgEpJ\n"\ + "uWkyESRYjV1iVO/b9uXVBaSKTUZpK5pzkpdwNKrUArTxP0wy25PmJaunluqA8ECv\n"\ + "e0pe69AzfIsaeOtJBFLZX2trF1alxXp93f16a1XuYSTkXgBU+TEap/y6GLfAcPUg\n"\ + "ALQWAlns5YPbYTjGIJWgN5RZkXC56bVA79nV737rAoGBAM9swaysVULI6A9lofIH\n"\ + "w/AgsIH0SotZt+1d7PJBww4ztrcqt66z56p13mUw0/zsIDxvPrhOS20hv3tymJyM\n"\ + "v3bwMnszsoZ1Kkj881GmnFtpMb6cwPfkbRJuY5DHfTdRhq+Tik5uYdRthlWM92Sf\n"\ + "Aogx44Fozd1t2hYcozPuZD4s\n"\ + "-----END PRIVATE KEY-----\n"; + + +static void test_leak_setup(const char *file, long line) +{ + uint8_t mac[6]; + struct timeval te; + gettimeofday(&te, NULL); // get current time + esp_read_mac(mac, ESP_MAC_WIFI_STA); + printf("%s:%ld: time=%ld.%lds, mac:" MACSTR "\n", file, line, te.tv_sec, te.tv_usec, MAC2STR(mac)); + // Execute esp_sha operation to allocate internal SHA semaphore memory + // which is considered as leaked otherwise + const uint8_t input_buffer[64]; + uint8_t output_buffer[64]; + esp_sha(SHA2_512, input_buffer, sizeof(input_buffer), output_buffer); + unity_reset_leak_checks(); +} + + +TEST_CASE("esp-tls init deinit", "[esp-tls][leaks=0]") +{ + test_leak_setup(__FILE__, __LINE__); + struct esp_tls *tls = esp_tls_init(); + TEST_ASSERT_NOT_NULL(tls); + int ret = esp_tls_conn_destroy(tls); + TEST_ASSERT_EQUAL(0, ret); +} + +TEST_CASE("esp-tls global_ca_store set free", "[esp-tls][leaks=0]") +{ + test_leak_setup(__FILE__, __LINE__); + esp_err_t ret = esp_tls_init_global_ca_store(); + TEST_ASSERT_EQUAL(ESP_OK, ret); + ret = esp_tls_set_global_ca_store((const unsigned char *)test_cert_pem, strlen(test_cert_pem) + 1); + TEST_ASSERT_EQUAL(ESP_OK, ret); + esp_tls_free_global_ca_store(); +} + +#ifdef CONFIG_ESP_TLS_SERVER +TEST_CASE("esp_tls_server session create delete", "[esp-tls][leaks=0]") +{ + test_leak_setup(__FILE__, __LINE__); + struct esp_tls *tls = esp_tls_init(); + TEST_ASSERT_NOT_NULL(tls); + esp_tls_cfg_server_t cfg = { + .servercert_buf = (const unsigned char *)test_cert_pem, + .servercert_bytes = strlen(test_cert_pem) + 1, + .serverkey_buf = (const unsigned char *)test_key_pem, + .serverkey_bytes = strlen(test_key_pem) + 1, + }; + // The call is going to fail at the time of TLS handshake (no internet), + // but the internal memory for the TLS connection will be allocated. + int ret = esp_tls_server_session_create(&cfg, -1, tls); + TEST_ASSERT_LESS_THAN_INT(0, ret); + // free the allocated memory. + esp_tls_server_session_delete(tls); +} +#endif diff --git a/tools/unit-test-app/configs/default_2 b/tools/unit-test-app/configs/default_2 index cd25f10ee8..0b2e01f386 100644 --- a/tools/unit-test-app/configs/default_2 +++ b/tools/unit-test-app/configs/default_2 @@ -1,3 +1,3 @@ # This config is split between targets since different component needs to be excluded (esp32, esp32s2) CONFIG_IDF_TARGET="esp32" -TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32 esp_ipc esp_pm esp_system esp_timer driver heap pthread soc spi_flash vfs test_utils experimental_cpp_component +TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32 esp_ipc esp_pm esp_system esp_timer driver heap pthread soc spi_flash vfs test_utils experimental_cpp_component esp-tls diff --git a/tools/unit-test-app/configs/default_2_c3 b/tools/unit-test-app/configs/default_2_c3 index 8727600286..fbd8f6d97b 100644 --- a/tools/unit-test-app/configs/default_2_c3 +++ b/tools/unit-test-app/configs/default_2_c3 @@ -1,3 +1,3 @@ # This config is split between targets since different component needs to be excluded (esp32, esp32s2) CONFIG_IDF_TARGET="esp32c3" -TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32c3 esp_ipc esp_pm esp_system esp_timer driver heap pthread soc spi_flash vfs experimental_cpp_component ulp perfmon +TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32c3 esp_ipc esp_pm esp_system esp_timer driver heap pthread soc spi_flash vfs experimental_cpp_component ulp perfmon esp-tls diff --git a/tools/unit-test-app/configs/default_2_s2 b/tools/unit-test-app/configs/default_2_s2 index 5ecc100d98..462f9802a2 100644 --- a/tools/unit-test-app/configs/default_2_s2 +++ b/tools/unit-test-app/configs/default_2_s2 @@ -1,3 +1,3 @@ # This config is split between targets since different component needs to be excluded (esp32, esp32s2) CONFIG_IDF_TARGET="esp32s2" -TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32s2 esp_ipc esp_pm esp_system esp_timer driver heap pthread soc spi_flash vfs experimental_cpp_component +TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32s2 esp_ipc esp_pm esp_system esp_timer driver heap pthread soc spi_flash vfs experimental_cpp_component esp-tls diff --git a/tools/unit-test-app/configs/esp_tls b/tools/unit-test-app/configs/esp_tls new file mode 100644 index 0000000000..87012382c5 --- /dev/null +++ b/tools/unit-test-app/configs/esp_tls @@ -0,0 +1,5 @@ +# As this is protocol specific, only test for one target. +CONFIG_IDF_TARGET="esp32" +TEST_COMPONENTS=esp-tls +TEST_EXCLUDE_COMPONENTS=libsodium bt +CONFIG_ESP_TLS_SERVER=y diff --git a/tools/unit-test-app/configs/psram b/tools/unit-test-app/configs/psram index dc72acfac7..d6577376c5 100644 --- a/tools/unit-test-app/configs/psram +++ b/tools/unit-test-app/configs/psram @@ -1,5 +1,5 @@ CONFIG_IDF_TARGET="esp32" -TEST_EXCLUDE_COMPONENTS=libsodium bt app_update driver esp32 esp_ipc esp_pm esp_system esp_timer mbedtls spi_flash test_utils heap pthread soc experimental_cpp_component +TEST_EXCLUDE_COMPONENTS=libsodium bt app_update driver esp32 esp_ipc esp_pm esp_system esp_timer mbedtls spi_flash test_utils heap pthread soc experimental_cpp_component esp-tls CONFIG_ESP32_SPIRAM_SUPPORT=y CONFIG_ESP_INT_WDT_TIMEOUT_MS=800 CONFIG_SPIRAM_OCCUPY_NO_HOST=y diff --git a/tools/unit-test-app/configs/release_2 b/tools/unit-test-app/configs/release_2 index 01e053fd04..f4a8f1c679 100644 --- a/tools/unit-test-app/configs/release_2 +++ b/tools/unit-test-app/configs/release_2 @@ -1,6 +1,6 @@ # This config is split between targets since different component needs to be included (esp32, esp32s2) CONFIG_IDF_TARGET="esp32" -TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32 esp_ipc esp_pm esp_system esp_timer driver heap pthread soc spi_flash vfs test_utils experimental_cpp_component +TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32 esp_ipc esp_pm esp_system esp_timer driver heap pthread soc spi_flash vfs test_utils experimental_cpp_component esp-tls CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/tools/unit-test-app/configs/single_core_2 b/tools/unit-test-app/configs/single_core_2 index 5ada2e06a7..6af65f72d4 100644 --- a/tools/unit-test-app/configs/single_core_2 +++ b/tools/unit-test-app/configs/single_core_2 @@ -1,6 +1,6 @@ # This config is split between targets since different component needs to be excluded (esp32, esp32s2) CONFIG_IDF_TARGET="esp32" -TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32 esp_system esp_pm esp_ipc esp_timer driver heap pthread soc spi_flash vfs test_utils experimental_cpp_component +TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32 esp_system esp_pm esp_ipc esp_timer driver heap pthread soc spi_flash vfs test_utils experimental_cpp_component esp-tls CONFIG_MEMMAP_SMP=n CONFIG_FREERTOS_UNICORE=y CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y From b06fa1d7c2d16c56bdb329685ff32a8aad9f97da Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Fri, 8 Jan 2021 16:56:28 +0530 Subject: [PATCH 5/5] https_request_example: Updated the example to showcase cacert_buf and global_ca store * Made required changes in the example_test --- .../protocols/https_request/example_test.py | 70 +++++-- .../https_request/main/CMakeLists.txt | 3 +- .../protocols/https_request/main/component.mk | 1 + .../main/https_request_example_main.c | 185 +++++++++++------- .../https_request/main/server_root_cert.pem | 27 +++ 5 files changed, 205 insertions(+), 81 deletions(-) create mode 100644 examples/protocols/https_request/main/server_root_cert.pem diff --git a/examples/protocols/https_request/example_test.py b/examples/protocols/https_request/example_test.py index 2e613dd2f4..5c6f937bd0 100644 --- a/examples/protocols/https_request/example_test.py +++ b/examples/protocols/https_request/example_test.py @@ -1,7 +1,7 @@ -import re import os - +import re import ttfw_idf +from tiny_test_fw import Utility @ttfw_idf.idf_example_test(env_tag="Example_EthKitV1") @@ -9,7 +9,8 @@ def test_examples_protocol_https_request(env, extra_data): """ steps: | 1. join AP - 2. connect to www.howsmyssl.com:443 + 2. establish TLS connection to www.howsmyssl.com:443 with multiple + certificate verification options 3. send http request """ dut1 = env.get_dut("https_request", "examples/protocols/https_request", dut_class=ttfw_idf.ESP32DUT) @@ -17,13 +18,51 @@ def test_examples_protocol_https_request(env, extra_data): binary_file = os.path.join(dut1.app.binary_path, "https_request.bin") bin_size = os.path.getsize(binary_file) ttfw_idf.log_performance("https_request_bin_size", "{}KB".format(bin_size // 1024)) - # start test + # start tes + Utility.console_log("Starting https_request simple test app") dut1.start_app() - dut1.expect("Connection established...", timeout=30) - dut1.expect("Reading HTTP response...") - dut1.expect(re.compile(r"Completed (\d) requests")) - # test mbedtls dynamic resource + # Check for connection using crt bundle + Utility.console_log("Testing for \"https_request using crt bundle\"") + try: + dut1.expect(re.compile("https_request using crt bundle"), timeout=30) + dut1.expect_all("Certificate validated", + "Connection established...", + "Reading HTTP response...", + "HTTP/1.1 200 OK", + re.compile("connection closed")) + except Exception: + Utility.console_log("Failed the test for \"https_request using crt bundle\"") + raise + Utility.console_log("Passed the test for \"https_request using crt bundle\"") + + # Check for connection using cacert_buf + Utility.console_log("Testing for \"https_request using cacert_buf\"") + try: + dut1.expect(re.compile("https_request using cacert_buf"), timeout=20) + dut1.expect_all("Connection established...", + "Reading HTTP response...", + "HTTP/1.1 200 OK", + re.compile("connection closed")) + except Exception: + Utility.console_log("Passed the test for \"https_request using cacert_buf\"") + raise + Utility.console_log("Passed the test for \"https_request using cacert_buf\"") + + # Check for connection using global ca_store + Utility.console_log("Testing for \"https_request using global ca_store\"") + try: + dut1.expect(re.compile("https_request using global ca_store"), timeout=20) + dut1.expect_all("Connection established...", + "Reading HTTP response...", + "HTTP/1.1 200 OK", + re.compile("connection closed")) + except Exception: + Utility.console_log("Failed the test for \"https_request using global ca_store\"") + raise + Utility.console_log("Passed the test for \"https_request using global ca_store\"") + + # Check for connection using crt bundle with mbedtls dynamic resource enabled dut1 = env.get_dut("https_request", "examples/protocols/https_request", dut_class=ttfw_idf.ESP32DUT, app_config_name='ssldyn') # check and log bin size binary_file = os.path.join(dut1.app.binary_path, "https_request.bin") @@ -31,9 +70,18 @@ def test_examples_protocol_https_request(env, extra_data): ttfw_idf.log_performance("https_request_bin_size", "{}KB".format(bin_size // 1024)) # start test dut1.start_app() - dut1.expect("Connection established...", timeout=30) - dut1.expect("Reading HTTP response...") - dut1.expect(re.compile(r"Completed (\d) requests")) + # only check if one connection is established + Utility.console_log("Testing for \"https_request using crt bundle\" with mbedtls dynamic resource enabled") + try: + dut1.expect(re.compile("https_request using crt bundle"), timeout=30) + dut1.expect_all("Connection established...", + "Reading HTTP response...", + "HTTP/1.1 200 OK", + re.compile("connection closed")) + except Exception: + Utility.console_log("Failed the test for \"https_request using crt bundle\" when mbedtls dynamic resource was enabled") + raise + Utility.console_log("Passed the test for \"https_request using crt bundle\" when mbedtls dynamic resource was enabled") if __name__ == '__main__': diff --git a/examples/protocols/https_request/main/CMakeLists.txt b/examples/protocols/https_request/main/CMakeLists.txt index 611302d838..4dfa9a4062 100644 --- a/examples/protocols/https_request/main/CMakeLists.txt +++ b/examples/protocols/https_request/main/CMakeLists.txt @@ -2,4 +2,5 @@ # # (If this was a component, we would set COMPONENT_EMBED_TXTFILES here.) idf_component_register(SRCS "https_request_example_main.c" - INCLUDE_DIRS ".") + INCLUDE_DIRS "." + EMBED_TXTFILES server_root_cert.pem) diff --git a/examples/protocols/https_request/main/component.mk b/examples/protocols/https_request/main/component.mk index a98f634eae..12acf9fa36 100644 --- a/examples/protocols/https_request/main/component.mk +++ b/examples/protocols/https_request/main/component.mk @@ -2,3 +2,4 @@ # "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) +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 index 5b37e9479f..d333145953 100644 --- a/examples/protocols/https_request/main/https_request_example_main.c +++ b/examples/protocols/https_request/main/https_request_example_main.c @@ -50,90 +50,137 @@ static const char *TAG = "example"; -static const char *REQUEST = "GET " WEB_URL " HTTP/1.1\r\n" - "Host: "WEB_SERVER"\r\n" - "User-Agent: esp-idf/1.0 esp32\r\n" - "\r\n"; +static const char REQUEST[] = "GET " WEB_URL " HTTP/1.1\r\n" + "Host: "WEB_SERVER"\r\n" + "User-Agent: esp-idf/1.0 esp32\r\n" + "\r\n"; -static void https_get_task(void *pvParameters) +/* 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 = 0) { + ESP_LOGI(TAG, "%d bytes written", ret); + written_bytes += ret; + } else if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) { + ESP_LOGE(TAG, "esp_tls_conn_write returned: [0x%02X](%s)", ret, esp_err_to_name(ret)); goto exit; } + } while (written_bytes < sizeof(REQUEST)); - 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 != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_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..."); - ESP_LOGI(TAG, "Reading HTTP response..."); + do { + len = sizeof(buf) - 1; + bzero(buf, sizeof(buf)); + ret = esp_tls_conn_read(tls, (char *)buf, len); - do - { - len = sizeof(buf) - 1; - bzero(buf, sizeof(buf)); - ret = esp_tls_conn_read(tls, (char *)buf, len); - - if(ret == ESP_TLS_ERR_SSL_WANT_WRITE || ret == ESP_TLS_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); + if (ret == ESP_TLS_ERR_SSL_WANT_WRITE || ret == ESP_TLS_ERR_SSL_WANT_READ) { + continue; } - ESP_LOGI(TAG, "Starting again!"); + + if (ret < 0) { + ESP_LOGE(TAG, "esp_tls_conn_read returned [-0x%02X](%s)", -ret, esp_err_to_name(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]); + } + putchar('\n'); // JSON output doesn't have a newline at end + } while (1); + +exit: + esp_tls_conn_delete(tls); + for (int countdown = 10; countdown >= 0; countdown--) { + ESP_LOGI(TAG, "%d...", countdown); + vTaskDelay(1000 / portTICK_PERIOD_MS); } } +static void https_get_request_using_crt_bundle(void) +{ + ESP_LOGI(TAG, "https_request using crt bundle"); + esp_tls_cfg_t cfg = { + .crt_bundle_attach = esp_crt_bundle_attach, + }; + https_get_request(cfg); +} + +static void https_get_request_using_cacert_buf(void) +{ + ESP_LOGI(TAG, "https_request using cacert_buf"); + esp_tls_cfg_t cfg = { + .cacert_buf = (const unsigned char *) server_root_cert_pem_start, + .cacert_bytes = server_root_cert_pem_end - server_root_cert_pem_start, + }; + https_get_request(cfg); +} + +static void https_get_request_using_global_ca_store(void) +{ + esp_err_t esp_ret = ESP_FAIL; + ESP_LOGI(TAG, "https_request using global ca_store"); + esp_ret = esp_tls_set_global_ca_store(server_root_cert_pem_start, server_root_cert_pem_end - server_root_cert_pem_start); + if (esp_ret != ESP_OK) { + ESP_LOGE(TAG, "Error in setting the global ca store: [%02X] (%s),could not complete the https_request using global_ca_store", esp_ret, esp_err_to_name(esp_ret)); + return; + } + esp_tls_cfg_t cfg = { + .use_global_ca_store = true, + }; + https_get_request(cfg); + esp_tls_free_global_ca_store(); +} + +static void https_request_task(void *pvparameters) +{ + ESP_LOGI(TAG, "Start https_request example"); + + https_get_request_using_crt_bundle(); + https_get_request_using_cacert_buf(); + https_get_request_using_global_ca_store(); + + ESP_LOGI(TAG, "Finish https_request example"); + vTaskDelete(NULL); +} + void app_main(void) { ESP_ERROR_CHECK( nvs_flash_init() ); @@ -146,5 +193,5 @@ void app_main(void) */ ESP_ERROR_CHECK(example_connect()); - xTaskCreate(&https_get_task, "https_get_task", 8192, NULL, 5, NULL); + xTaskCreate(&https_request_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 0000000000..0002462ce8 --- /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-----