diff --git a/components/esp_https_ota/include/esp_https_ota.h b/components/esp_https_ota/include/esp_https_ota.h index da7ad89a5b..af77b07971 100644 --- a/components/esp_https_ota/include/esp_https_ota.h +++ b/components/esp_https_ota/include/esp_https_ota.h @@ -62,6 +62,7 @@ typedef struct { #if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB decrypt_cb_t decrypt_cb; /*!< Callback for external decryption layer */ void *decrypt_user_ctx; /*!< User context for external decryption layer */ + uint16_t enc_img_header_size; /*!< Header size of pre-encrypted ota image header */ #endif } esp_https_ota_config_t; diff --git a/components/esp_https_ota/src/esp_https_ota.c b/components/esp_https_ota/src/esp_https_ota.c index 5ae44ff162..df1e928f7a 100644 --- a/components/esp_https_ota/src/esp_https_ota.c +++ b/components/esp_https_ota/src/esp_https_ota.c @@ -53,6 +53,7 @@ struct esp_https_ota_handle { #if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB decrypt_cb_t decrypt_cb; void *decrypt_user_ctx; + uint16_t enc_img_header_size; #endif }; @@ -323,6 +324,11 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http } https_ota_handle->image_length = esp_http_client_get_content_length(https_ota_handle->http_client); +#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB + /* In case of pre ecnrypted OTA, actual image size of OTA binary includes header size + which stored in variable enc_img_header_size*/ + https_ota_handle->image_length -= ota_config->enc_img_header_size; +#endif esp_http_client_close(https_ota_handle->http_client); if (https_ota_handle->image_length > https_ota_handle->max_http_request_size) { @@ -349,6 +355,11 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http if (!https_ota_handle->partial_http_download) { https_ota_handle->image_length = esp_http_client_get_content_length(https_ota_handle->http_client); +#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB + /* In case of pre ecnrypted OTA, actual image size of OTA binary includes header size + which stored in variable enc_img_header_size*/ + https_ota_handle->image_length -= ota_config->enc_img_header_size; +#endif } https_ota_handle->update_partition = NULL; @@ -376,6 +387,7 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http } https_ota_handle->decrypt_cb = ota_config->decrypt_cb; https_ota_handle->decrypt_user_ctx = ota_config->decrypt_user_ctx; + https_ota_handle->enc_img_header_size = ota_config->enc_img_header_size; #endif https_ota_handle->ota_upgrade_buf_size = alloc_size; https_ota_handle->bulk_flash_erase = ota_config->bulk_flash_erase; @@ -584,10 +596,14 @@ esp_err_t esp_https_ota_perform(esp_https_ota_handle_t https_ota_handle) if (handle->state == ESP_HTTPS_OTA_IN_PROGRESS && handle->image_length > handle->binary_file_len) { esp_http_client_close(handle->http_client); char *header_val = NULL; + int header_size = 0; +#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB + header_size = handle->enc_img_header_size; +#endif if ((handle->image_length - handle->binary_file_len) > handle->max_http_request_size) { - asprintf(&header_val, "bytes=%d-%d", handle->binary_file_len, (handle->binary_file_len + handle->max_http_request_size - 1)); + asprintf(&header_val, "bytes=%d-%d", handle->binary_file_len + header_size, (handle->binary_file_len + header_size + handle->max_http_request_size - 1)); } else { - asprintf(&header_val, "bytes=%d-", handle->binary_file_len); + asprintf(&header_val, "bytes=%d-", handle->binary_file_len + header_size); } if (header_val == NULL) { ESP_LOGE(TAG, "Failed to allocate memory for HTTP header"); diff --git a/examples/system/ota/pre_encrypted_ota/main/pre_encrypted_ota.c b/examples/system/ota/pre_encrypted_ota/main/pre_encrypted_ota.c index caa9335f7f..599838a2f1 100644 --- a/examples/system/ota/pre_encrypted_ota/main/pre_encrypted_ota.c +++ b/examples/system/ota/pre_encrypted_ota/main/pre_encrypted_ota.c @@ -74,6 +74,7 @@ static esp_err_t _decrypt_cb(decrypt_cb_arg_t *args, void *user_ctx) if (err != ESP_OK && err != ESP_ERR_NOT_FINISHED) { return err; } + static bool is_image_verified = false; if (pargs.data_out_len > 0) { args->data_out = pargs.data_out; @@ -143,6 +144,7 @@ void pre_encrypted_ota_task(void *pvParameter) #endif .decrypt_cb = _decrypt_cb, .decrypt_user_ctx = (void *)decrypt_handle, + .enc_img_header_size = esp_encrypted_img_get_header_size(), }; esp_https_ota_handle_t https_ota_handle = NULL; diff --git a/examples/system/ota/pre_encrypted_ota/pytest_pre_encrypted_ota.py b/examples/system/ota/pre_encrypted_ota/pytest_pre_encrypted_ota.py index 58286eefbf..b378251b6e 100644 --- a/examples/system/ota/pre_encrypted_ota/pytest_pre_encrypted_ota.py +++ b/examples/system/ota/pre_encrypted_ota/pytest_pre_encrypted_ota.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Unlicense OR CC0-1.0 import http.server import multiprocessing @@ -82,3 +82,47 @@ def test_examples_protocol_pre_encrypted_ota_example(dut: Dut) -> None: dut.expect('Loaded app from partition at offset', timeout=30) finally: thread1.terminate() + + +@pytest.mark.esp32 +@pytest.mark.ethernet_ota +@pytest.mark.parametrize('config', ['partial_download',], indirect=True) +def test_examples_protocol_pre_encrypted_ota_example_partial_request(dut: Dut) -> None: + server_port = 8001 + # Size of partial HTTP request + request_size = int(dut.app.sdkconfig.get('EXAMPLE_HTTP_REQUEST_SIZE')) + # File to be downloaded. This file is generated after compilation + binary_file = os.path.join(dut.app.binary_path, enc_bin_name) + bin_size = os.path.getsize(binary_file) + http_requests = int((bin_size / request_size) - 1) + assert http_requests > 1 + + # Start server + thread1 = multiprocessing.Process(target=start_https_server, args=(dut.app.binary_path, '0.0.0.0', server_port)) + thread1.daemon = True + thread1.start() + + try: + dut.expect('Loaded app from partition at offset', timeout=30) + try: + ip_address = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=30)[1].decode() + print('Connected to AP/Ethernet with IP: {}'.format(ip_address)) + except pexpect.exceptions.TIMEOUT: + raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP/Ethernet') + host_ip = get_host_ip4_by_dest_ip(ip_address) + + dut.expect('Starting Pre Encrypted OTA example', timeout=30) + print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + enc_bin_name)) + dut.write('https://' + host_ip + ':' + str(server_port) + '/' + enc_bin_name) + dut.expect('Magic Verified', timeout=30) + dut.expect('Reading RSA private key', timeout=30) + + for _ in range(http_requests): + dut.expect('Connection closed', timeout=60) + + dut.expect('upgrade successful. Rebooting', timeout=60) + # after reboot + dut.expect('Loaded app from partition at offset', timeout=30) + + finally: + thread1.terminate() diff --git a/examples/system/ota/pre_encrypted_ota/sdkconfig.ci.partial_download b/examples/system/ota/pre_encrypted_ota/sdkconfig.ci.partial_download new file mode 100644 index 0000000000..6699158c38 --- /dev/null +++ b/examples/system/ota/pre_encrypted_ota/sdkconfig.ci.partial_download @@ -0,0 +1,19 @@ +CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL="FROM_STDIN" +CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK=y +CONFIG_EXAMPLE_SKIP_VERSION_CHECK=y +CONFIG_EXAMPLE_OTA_RECV_TIMEOUT=3000 +CONFIG_EXAMPLE_ENABLE_PARTIAL_HTTP_DOWNLOAD=y +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y + +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_IP101=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 + +CONFIG_MBEDTLS_TLS_CLIENT_ONLY=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_EXAMPLE_CONNECT_IPV6=n