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