mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/pre_encrypted_binary' into 'master'
feature: Pre Encrypted Binary during OTA updates Closes IDF-3256, IDF-3254, and IDFGH-4334 See merge request espressif/esp-idf!16434
This commit is contained in:
commit
69f51a989c
@ -1,6 +1,15 @@
|
||||
menu "ESP HTTPS OTA"
|
||||
|
||||
config OTA_ALLOW_HTTP
|
||||
config ESP_HTTPS_OTA_DECRYPT_CB
|
||||
bool "Provide decryption callback"
|
||||
default n
|
||||
help
|
||||
Exposes an additional callback whereby firmware data could be decrypted
|
||||
before being processed by OTA update component. This can help to integrate
|
||||
external encryption related format and removal of such encapsulation layer
|
||||
from firmware image.
|
||||
|
||||
config ESP_HTTPS_OTA_ALLOW_HTTP
|
||||
bool "Allow HTTP for OTA (WARNING: ONLY FOR TESTING PURPOSE, READ HELP)"
|
||||
default n
|
||||
help
|
||||
|
@ -1,21 +1,14 @@
|
||||
// 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.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <esp_http_client.h>
|
||||
#include <bootloader_common.h>
|
||||
#include <sdkconfig.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -24,6 +17,17 @@ extern "C" {
|
||||
typedef void *esp_https_ota_handle_t;
|
||||
typedef esp_err_t(*http_client_init_cb_t)(esp_http_client_handle_t);
|
||||
|
||||
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
typedef struct {
|
||||
const char *data_in; /*!< Pointer to data to be decrypted */
|
||||
size_t data_in_len; /*!< Input data length */
|
||||
char *data_out; /*!< Pointer to data decrypted using callback, this will be freed after data is written to flash */
|
||||
size_t data_out_len; /*!< Output data length */
|
||||
} decrypt_cb_arg_t;
|
||||
|
||||
typedef esp_err_t(*decrypt_cb_t)(decrypt_cb_arg_t *args);
|
||||
#endif // CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
|
||||
/**
|
||||
* @brief ESP HTTPS OTA configuration
|
||||
*/
|
||||
@ -33,6 +37,9 @@ typedef struct {
|
||||
bool bulk_flash_erase; /*!< Erase entire flash partition during initialization. By default flash partition is erased during write operation and in chunk of 4K sector size */
|
||||
bool partial_http_download; /*!< Enable Firmware image to be downloaded over multiple HTTP requests */
|
||||
int max_http_request_size; /*!< Maximum request size for partial HTTP download */
|
||||
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
decrypt_cb_t decrypt_cb; /*!< Callback for external decryption layer */
|
||||
#endif
|
||||
} esp_https_ota_config_t;
|
||||
|
||||
#define ESP_ERR_HTTPS_OTA_BASE (0x9000)
|
||||
@ -108,6 +115,7 @@ esp_err_t esp_https_ota_begin(esp_https_ota_config_t *ota_config, esp_https_ota_
|
||||
* - ESP_OK: OTA update was successful
|
||||
* - ESP_FAIL: OTA update failed
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - ESP_ERR_INVALID_VERSION: Invalid chip revision in image header
|
||||
* - ESP_ERR_OTA_VALIDATE_FAILED: Invalid app image
|
||||
* - ESP_ERR_NO_MEM: Cannot allocate memory for OTA operation.
|
||||
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
|
||||
@ -181,6 +189,7 @@ esp_err_t esp_https_ota_abort(esp_https_ota_handle_t https_ota_handle);
|
||||
*
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG: Invalid arguments
|
||||
* - ESP_ERR_INVALID_STATE: Invalid state to call this API. esp_https_ota_begin() not called yet.
|
||||
* - ESP_FAIL: Failed to read image descriptor
|
||||
* - ESP_OK: Successfully read image descriptor
|
||||
*/
|
||||
|
4
components/esp_https_ota/sdkconfig.rename
Normal file
4
components/esp_https_ota/sdkconfig.rename
Normal file
@ -0,0 +1,4 @@
|
||||
# sdkconfig replacement configurations for deprecated options formatted as
|
||||
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
|
||||
|
||||
CONFIG_OTA_ALLOW_HTTP CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP
|
@ -13,8 +13,14 @@
|
||||
#include <errno.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#define IMAGE_HEADER_SIZE sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t) + 1
|
||||
#define DEFAULT_OTA_BUF_SIZE IMAGE_HEADER_SIZE
|
||||
#define IMAGE_HEADER_SIZE (1024)
|
||||
|
||||
/* This is kept sufficiently large enough to cover image format headers
|
||||
* and also this defines default minimum OTA buffer chunk size */
|
||||
#define DEFAULT_OTA_BUF_SIZE (IMAGE_HEADER_SIZE)
|
||||
|
||||
_Static_assert(DEFAULT_OTA_BUF_SIZE > (sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t) + 1), "OTA data buffer too small");
|
||||
|
||||
#define DEFAULT_REQUEST_SIZE (64 * 1024)
|
||||
static const char *TAG = "esp_https_ota";
|
||||
|
||||
@ -37,6 +43,9 @@ struct esp_https_ota_handle {
|
||||
esp_https_ota_state state;
|
||||
bool bulk_flash_erase;
|
||||
bool partial_http_download;
|
||||
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
decrypt_cb_t decrypt_cb;
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef struct esp_https_ota_handle esp_https_ota_t;
|
||||
@ -77,7 +86,7 @@ static esp_err_t _http_handle_response_code(esp_http_client_handle_t http_client
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
char upgrade_data_buf[DEFAULT_OTA_BUF_SIZE];
|
||||
char upgrade_data_buf[256];
|
||||
// process_again() returns true only in case of redirection.
|
||||
if (process_again(status_code)) {
|
||||
while (1) {
|
||||
@ -85,7 +94,7 @@ static esp_err_t _http_handle_response_code(esp_http_client_handle_t http_client
|
||||
* In case of redirection, esp_http_client_read() is called
|
||||
* to clear the response buffer of http_client.
|
||||
*/
|
||||
int data_read = esp_http_client_read(http_client, upgrade_data_buf, DEFAULT_OTA_BUF_SIZE);
|
||||
int data_read = esp_http_client_read(http_client, upgrade_data_buf, sizeof(upgrade_data_buf));
|
||||
if (data_read <= 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
@ -141,6 +150,27 @@ static void _http_cleanup(esp_http_client_handle_t client)
|
||||
esp_http_client_cleanup(client);
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
static esp_err_t esp_https_ota_decrypt_cb(esp_https_ota_t *handle, decrypt_cb_arg_t *args)
|
||||
{
|
||||
esp_err_t ret = handle->decrypt_cb(args);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Decrypt callback failed %d", ret);
|
||||
return ret;
|
||||
}
|
||||
if (args->data_out_len > 0) {
|
||||
return ESP_OK;
|
||||
} else {
|
||||
return ESP_HTTPS_OTA_IN_PROGRESS;
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_https_ota_decrypt_cb_free_buf(void *buffer)
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
#endif // CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
|
||||
static esp_err_t _ota_write(esp_https_ota_t *https_ota_handle, const void *buffer, size_t buf_len)
|
||||
{
|
||||
if (buffer == NULL || https_ota_handle == NULL) {
|
||||
@ -154,6 +184,9 @@ static esp_err_t _ota_write(esp_https_ota_t *https_ota_handle, const void *buffe
|
||||
ESP_LOGD(TAG, "Written image length %d", https_ota_handle->binary_file_len);
|
||||
err = ESP_ERR_HTTPS_OTA_IN_PROGRESS;
|
||||
}
|
||||
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
esp_https_ota_decrypt_cb_free_buf((void *) buffer);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -176,8 +209,8 @@ esp_err_t esp_https_ota_begin(esp_https_ota_config_t *ota_config, esp_https_ota_
|
||||
}
|
||||
|
||||
if (!is_server_verification_enabled(ota_config)) {
|
||||
#if CONFIG_OTA_ALLOW_HTTP
|
||||
ESP_LOGW(TAG, "Continuing with insecure option because CONFIG_OTA_ALLOW_HTTP is set.");
|
||||
#if CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP
|
||||
ESP_LOGW(TAG, "Continuing with insecure option because CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP is set.");
|
||||
#else
|
||||
ESP_LOGE(TAG, "No option for server verification is enabled in esp_http_client config.");
|
||||
*handle = NULL;
|
||||
@ -271,6 +304,13 @@ esp_err_t esp_https_ota_begin(esp_https_ota_config_t *ota_config, esp_https_ota_
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto http_cleanup;
|
||||
}
|
||||
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
if (ota_config->decrypt_cb == NULL) {
|
||||
err = ESP_ERR_INVALID_ARG;
|
||||
goto http_cleanup;
|
||||
}
|
||||
https_ota_handle->decrypt_cb = ota_config->decrypt_cb;
|
||||
#endif
|
||||
https_ota_handle->ota_upgrade_buf_size = alloc_size;
|
||||
https_ota_handle->bulk_flash_erase = ota_config->bulk_flash_erase;
|
||||
https_ota_handle->binary_file_len = 0;
|
||||
@ -330,18 +370,26 @@ esp_err_t esp_https_ota_get_img_desc(esp_https_ota_handle_t https_ota_handle, es
|
||||
}
|
||||
if (handle->state < ESP_HTTPS_OTA_BEGIN) {
|
||||
ESP_LOGE(TAG, "esp_https_ota_read_img_desc: Invalid state");
|
||||
return ESP_FAIL;
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (read_header(handle) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
memcpy(new_app_info, &handle->ota_upgrade_buf[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
|
||||
|
||||
const int app_desc_offset = sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t);
|
||||
esp_app_desc_t *app_info = (esp_app_desc_t *) &handle->ota_upgrade_buf[app_desc_offset];
|
||||
if (app_info->magic_word != ESP_APP_DESC_MAGIC_WORD) {
|
||||
ESP_LOGE(TAG, "Incorrect app descriptor magic");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
memcpy(new_app_info, app_info, sizeof(esp_app_desc_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_ota_verify_chip_id(void *arg)
|
||||
static esp_err_t esp_ota_verify_chip_id(const void *arg)
|
||||
{
|
||||
esp_image_header_t *data = (esp_image_header_t*)(arg);
|
||||
esp_image_header_t *data = (esp_image_header_t *)(arg);
|
||||
if (data->chip_id != CONFIG_IDF_FIRMWARE_CHIP_ID) {
|
||||
ESP_LOGE(TAG, "Mismatch chip id, expected %d, found %d", CONFIG_IDF_FIRMWARE_CHIP_ID, data->chip_id);
|
||||
return ESP_ERR_INVALID_VERSION;
|
||||
@ -389,11 +437,26 @@ esp_err_t esp_https_ota_perform(esp_https_ota_handle_t https_ota_handle)
|
||||
}
|
||||
binary_file_len = IMAGE_HEADER_SIZE;
|
||||
}
|
||||
err = esp_ota_verify_chip_id(handle->ota_upgrade_buf);
|
||||
|
||||
const void *data_buf = (const void *) handle->ota_upgrade_buf;
|
||||
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
decrypt_cb_arg_t args = {};
|
||||
args.data_in = handle->ota_upgrade_buf;
|
||||
args.data_in_len = binary_file_len;
|
||||
err = esp_https_ota_decrypt_cb(handle, &args);
|
||||
if (err == ESP_OK) {
|
||||
data_buf = args.data_out;
|
||||
binary_file_len = args.data_out_len;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Decryption of image header failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif // CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
err = esp_ota_verify_chip_id(data_buf);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
return _ota_write(handle, (const void *)handle->ota_upgrade_buf, binary_file_len);
|
||||
return _ota_write(handle, data_buf, binary_file_len);
|
||||
case ESP_HTTPS_OTA_IN_PROGRESS:
|
||||
data_read = esp_http_client_read(handle->http_client,
|
||||
handle->ota_upgrade_buf,
|
||||
@ -419,7 +482,21 @@ esp_err_t esp_https_ota_perform(esp_https_ota_handle_t https_ota_handle)
|
||||
}
|
||||
ESP_LOGD(TAG, "Connection closed");
|
||||
} else if (data_read > 0) {
|
||||
return _ota_write(handle, (const void *)handle->ota_upgrade_buf, data_read);
|
||||
const void *data_buf = (const void *) handle->ota_upgrade_buf;
|
||||
int data_len = data_read;
|
||||
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
decrypt_cb_arg_t args = {};
|
||||
args.data_in = handle->ota_upgrade_buf;
|
||||
args.data_in_len = data_read;
|
||||
err = esp_https_ota_decrypt_cb(handle, &args);
|
||||
if (err == ESP_OK) {
|
||||
data_buf = args.data_out;
|
||||
data_len = args.data_out_len;
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
#endif // CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
return _ota_write(handle, data_buf, data_len);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "data read %d, errno %d", data_read, errno);
|
||||
return ESP_FAIL;
|
||||
|
@ -146,7 +146,7 @@ Running a local https server might be tricky in some cases (due to self signed c
|
||||
* Run a plain HTTP server to test the connection. (Note that using a plain http is **not secure** and should only be used for testing)
|
||||
- Execute `python -m http.server 8070` in the directory with the firmware image
|
||||
- Use http://<host-ip>:8070/<firmware-name> as the firmware upgrade URL
|
||||
- Enable *Allow HTTP for OTA* (`CONFIG_OTA_ALLOW_HTTP`) in `Component config -> ESP HTTPS OTA` so the URI without TLS is accepted
|
||||
- Enable *Allow HTTP for OTA* (`CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP`) in `Component config -> ESP HTTPS OTA` so the URI without TLS is accepted
|
||||
* Start the https server using [example_test](simple_ota_example/example_test.py) with two or more parameters: `example_test.py <BIN_DIR> <PORT> [CERT_DIR]`, where:
|
||||
- `<BIN_DIR>` is a directory containing the image and by default also the certificate and key files:`ca_cert.pem` and `ca_key.pem`
|
||||
- `<PORT>` is the server's port, here `8070`
|
||||
|
@ -138,8 +138,8 @@ def test_examples_protocol_advanced_https_ota_example(env, extra_data):
|
||||
ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
thread1.terminate()
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
dut1.expect('Starting Advanced OTA example', timeout=30)
|
||||
|
||||
print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name))
|
||||
@ -172,11 +172,10 @@ def test_examples_protocol_advanced_https_ota_example_truncated_bin(env, extra_d
|
||||
truncated_bin_size = 64000
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, bin_name)
|
||||
f = open(binary_file, 'rb+')
|
||||
fo = open(os.path.join(dut1.app.binary_path, truncated_bin_name), 'wb+')
|
||||
fo.write(f.read(truncated_bin_size))
|
||||
fo.close()
|
||||
f.close()
|
||||
with open(binary_file, 'rb+') as f:
|
||||
with open(os.path.join(dut1.app.binary_path, truncated_bin_name), 'wb+') as fo:
|
||||
fo.write(f.read(truncated_bin_size))
|
||||
|
||||
binary_file = os.path.join(dut1.app.binary_path, truncated_bin_name)
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
|
||||
@ -192,14 +191,17 @@ def test_examples_protocol_advanced_https_ota_example_truncated_bin(env, extra_d
|
||||
ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
thread1.terminate()
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
dut1.expect('Starting Advanced OTA example', timeout=30)
|
||||
|
||||
print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + truncated_bin_name))
|
||||
dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + truncated_bin_name)
|
||||
dut1.expect('Image validation failed, image is corrupted', timeout=30)
|
||||
os.remove(binary_file)
|
||||
try:
|
||||
os.remove(binary_file)
|
||||
except OSError:
|
||||
pass
|
||||
thread1.terminate()
|
||||
|
||||
|
||||
@ -224,11 +226,10 @@ def test_examples_protocol_advanced_https_ota_example_truncated_header(env, extr
|
||||
truncated_bin_size = 180
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, bin_name)
|
||||
f = open(binary_file, 'rb+')
|
||||
fo = open(os.path.join(dut1.app.binary_path, truncated_bin_name), 'wb+')
|
||||
fo.write(f.read(truncated_bin_size))
|
||||
fo.close()
|
||||
f.close()
|
||||
with open(binary_file, 'rb+') as f:
|
||||
with open(os.path.join(dut1.app.binary_path, truncated_bin_name), 'wb+') as fo:
|
||||
fo.write(f.read(truncated_bin_size))
|
||||
|
||||
binary_file = os.path.join(dut1.app.binary_path, truncated_bin_name)
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
|
||||
@ -244,14 +245,17 @@ def test_examples_protocol_advanced_https_ota_example_truncated_header(env, extr
|
||||
ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
thread1.terminate()
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
dut1.expect('Starting Advanced OTA example', timeout=30)
|
||||
|
||||
print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + truncated_bin_name))
|
||||
dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + truncated_bin_name)
|
||||
dut1.expect('advanced_https_ota_example: esp_https_ota_read_img_desc failed', timeout=30)
|
||||
os.remove(binary_file)
|
||||
try:
|
||||
os.remove(binary_file)
|
||||
except OSError:
|
||||
pass
|
||||
thread1.terminate()
|
||||
|
||||
|
||||
@ -274,13 +278,13 @@ def test_examples_protocol_advanced_https_ota_example_random(env, extra_data):
|
||||
random_bin_size = 32000
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, random_bin_name)
|
||||
fo = open(binary_file, 'wb+')
|
||||
# First byte of binary file is always set to zero. If first byte is generated randomly,
|
||||
# in some cases it may generate 0xE9 which will result in failure of testcase.
|
||||
fo.write(struct.pack('B', 0))
|
||||
for i in range(random_bin_size - 1):
|
||||
fo.write(struct.pack('B', random.randrange(0,255,1)))
|
||||
fo.close()
|
||||
with open(binary_file, 'wb+') as fo:
|
||||
# First byte of binary file is always set to zero. If first byte is generated randomly,
|
||||
# in some cases it may generate 0xE9 which will result in failure of testcase.
|
||||
fo.write(struct.pack('B', 0))
|
||||
for i in range(random_bin_size - 1):
|
||||
fo.write(struct.pack('B', random.randrange(0,255,1)))
|
||||
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
|
||||
# start test
|
||||
@ -295,14 +299,71 @@ def test_examples_protocol_advanced_https_ota_example_random(env, extra_data):
|
||||
ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
thread1.terminate()
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
dut1.expect('Starting Advanced OTA example', timeout=30)
|
||||
|
||||
print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + random_bin_name))
|
||||
dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + random_bin_name)
|
||||
dut1.expect(re.compile(r'esp_https_ota: Incorrect app descriptor magic'), timeout=10)
|
||||
try:
|
||||
os.remove(binary_file)
|
||||
except OSError:
|
||||
pass
|
||||
thread1.terminate()
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='EXAMPLE_ETH_OTA')
|
||||
def test_examples_protocol_advanced_https_ota_example_invalid_chip_id(env, extra_data):
|
||||
"""
|
||||
Working of OTA if binary file have invalid chip id is validated in this test case.
|
||||
Chip id verification should fail in this case.
|
||||
steps: |
|
||||
1. join AP
|
||||
2. Generate binary image with invalid chip id
|
||||
3. Fetch OTA image over HTTPS
|
||||
4. Check working of code for random binary file
|
||||
"""
|
||||
dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
|
||||
server_port = 8001
|
||||
bin_name = 'advanced_https_ota.bin'
|
||||
# Random binary file to be generated
|
||||
random_bin_name = 'random.bin'
|
||||
random_binary_file = os.path.join(dut1.app.binary_path, random_bin_name)
|
||||
# Size of random binary file. 2000 is choosen, to reduce the time required to run the test-case
|
||||
random_bin_size = 2000
|
||||
|
||||
binary_file = os.path.join(dut1.app.binary_path, bin_name)
|
||||
with open(binary_file, 'rb+') as f:
|
||||
data = list(f.read(random_bin_size))
|
||||
# Changing Chip id
|
||||
data[13] = 0xfe
|
||||
with open(random_binary_file, 'wb+') as fo:
|
||||
fo.write(bytearray(data))
|
||||
|
||||
# start test
|
||||
host_ip = get_my_ip()
|
||||
if (get_server_status(host_ip, server_port) is False):
|
||||
thread1 = multiprocessing.Process(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
|
||||
thread1.daemon = True
|
||||
thread1.start()
|
||||
dut1.start_app()
|
||||
dut1.expect('Loaded app from partition at offset', timeout=30)
|
||||
try:
|
||||
ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
thread1.terminate()
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
dut1.expect('Starting Advanced OTA example', timeout=30)
|
||||
|
||||
print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + random_bin_name))
|
||||
dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + random_bin_name)
|
||||
dut1.expect(re.compile(r'esp_https_ota: Mismatch chip id, expected 0, found \d'), timeout=10)
|
||||
os.remove(binary_file)
|
||||
try:
|
||||
os.remove(random_binary_file)
|
||||
except OSError:
|
||||
pass
|
||||
thread1.terminate()
|
||||
|
||||
|
||||
@ -378,9 +439,9 @@ def test_examples_protocol_advanced_https_ota_example_redirect_url(env, extra_da
|
||||
ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
thread1.terminate()
|
||||
thread2.terminate()
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
dut1.expect('Starting Advanced OTA example', timeout=30)
|
||||
|
||||
print('writing to device: {}'.format('https://' + host_ip + ':' + str(redirection_server_port) + '/' + bin_name))
|
||||
@ -415,14 +476,12 @@ def test_examples_protocol_advanced_https_ota_example_anti_rollback(env, extra_d
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, bin_name)
|
||||
file_size = os.path.getsize(binary_file)
|
||||
f = open(binary_file, 'rb+')
|
||||
fo = open(os.path.join(dut1.app.binary_path, anti_rollback_bin_name), 'wb+')
|
||||
fo.write(f.read(file_size))
|
||||
# Change security_version to 0 for negative test case
|
||||
fo.seek(36)
|
||||
fo.write(b'\x00')
|
||||
fo.close()
|
||||
f.close()
|
||||
with open(binary_file, 'rb+') as f:
|
||||
with open(os.path.join(dut1.app.binary_path, anti_rollback_bin_name), 'wb+') as fo:
|
||||
fo.write(f.read(file_size))
|
||||
# Change security_version to 0 for negative test case
|
||||
fo.seek(36)
|
||||
fo.write(b'\x00')
|
||||
binary_file = os.path.join(dut1.app.binary_path, anti_rollback_bin_name)
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
|
||||
@ -439,8 +498,8 @@ def test_examples_protocol_advanced_https_ota_example_anti_rollback(env, extra_d
|
||||
ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
thread1.terminate()
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
dut1.expect('Starting Advanced OTA example', timeout=30)
|
||||
|
||||
# Use originally generated image with secure_version=1
|
||||
@ -456,7 +515,10 @@ def test_examples_protocol_advanced_https_ota_example_anti_rollback(env, extra_d
|
||||
print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + anti_rollback_bin_name))
|
||||
dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + anti_rollback_bin_name)
|
||||
dut1.expect('New firmware security version is less than eFuse programmed, 0 < 1', timeout=30)
|
||||
os.remove(binary_file)
|
||||
try:
|
||||
os.remove(binary_file)
|
||||
except OSError:
|
||||
pass
|
||||
thread1.terminate()
|
||||
|
||||
|
||||
@ -493,8 +555,8 @@ def test_examples_protocol_advanced_https_ota_example_partial_request(env, extra
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
Utility.console_log('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
raise
|
||||
thread1.terminate()
|
||||
raise
|
||||
dut1.expect('Starting Advanced OTA example', timeout=30)
|
||||
|
||||
print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name))
|
||||
@ -537,8 +599,8 @@ def test_examples_protocol_advanced_https_ota_example_nimble_gatts(env, extra_da
|
||||
ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
thread1.terminate()
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
|
||||
dut1.expect('Starting Advanced OTA example', timeout=30)
|
||||
print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name))
|
||||
@ -581,8 +643,8 @@ def test_examples_protocol_advanced_https_ota_example_bluedroid_gatts(env, extra
|
||||
ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
thread1.terminate()
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
|
||||
dut1.expect('Starting Advanced OTA example', timeout=30)
|
||||
print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name))
|
||||
@ -616,13 +678,11 @@ def test_examples_protocol_advanced_https_ota_example_openssl_aligned_bin(env, e
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
# Dummy data required to align binary size to 289 bytes boundary
|
||||
dummy_data_size = 289 - (bin_size % 289)
|
||||
f = open(binary_file, 'rb+')
|
||||
fo = open(os.path.join(dut1.app.binary_path, aligned_bin_name), 'wb+')
|
||||
fo.write(f.read(bin_size))
|
||||
for _ in range(dummy_data_size):
|
||||
fo.write(struct.pack('B', random.randrange(0,255,1)))
|
||||
fo.close()
|
||||
f.close()
|
||||
with open(binary_file, 'rb+') as f:
|
||||
with open(os.path.join(dut1.app.binary_path, aligned_bin_name), 'wb+') as fo:
|
||||
fo.write(f.read(bin_size))
|
||||
for _ in range(dummy_data_size):
|
||||
fo.write(struct.pack('B', random.randrange(0,255,1)))
|
||||
# start test
|
||||
host_ip = get_my_ip()
|
||||
chunked_server = start_chunked_server(dut1.app.binary_path, 8070)
|
||||
@ -640,7 +700,10 @@ def test_examples_protocol_advanced_https_ota_example_openssl_aligned_bin(env, e
|
||||
dut1.expect('Loaded app from partition at offset', timeout=60)
|
||||
dut1.expect('Starting Advanced OTA example', timeout=30)
|
||||
chunked_server.kill()
|
||||
os.remove(aligned_bin_name)
|
||||
try:
|
||||
os.remove(aligned_bin_name)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@ -650,6 +713,7 @@ if __name__ == '__main__':
|
||||
test_examples_protocol_advanced_https_ota_example_truncated_bin()
|
||||
test_examples_protocol_advanced_https_ota_example_truncated_header()
|
||||
test_examples_protocol_advanced_https_ota_example_random()
|
||||
test_examples_protocol_advanced_https_ota_example_invalid_chip_id()
|
||||
test_examples_protocol_advanced_https_ota_example_anti_rollback()
|
||||
test_examples_protocol_advanced_https_ota_example_partial_request()
|
||||
test_examples_protocol_advanced_https_ota_example_nimble_gatts()
|
||||
|
9
examples/system/ota/pre_encrypted_ota/CMakeLists.txt
Normal file
9
examples/system/ota/pre_encrypted_ota/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
# For more information about build system see
|
||||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||
# 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)
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(pre_encrypted_ota)
|
41
examples/system/ota/pre_encrypted_ota/README.md
Normal file
41
examples/system/ota/pre_encrypted_ota/README.md
Normal file
@ -0,0 +1,41 @@
|
||||
|
||||
# Encrypted Binary OTA
|
||||
|
||||
This example demonstrates OTA updates with pre-encrypted binary using `esp_encrypted_img` component's APIs and tool. Pre encrypted firmware binary must be hosted on OTA update server. This firmware will be fetched and then decrypted on device before being flashed. This allows firmware to remain `confidential` on the OTA update channel irrespective of underlying transport (e.g., non-TLS).
|
||||
|
||||
## ESP Encrypted Image Abstraction Layer
|
||||
|
||||
This example uses `esp_encrypted_img` component hosted at https://github.com/espressif/idf-extra-components/blob/master/esp_encrypted_img through component manager. Please refer to its documentation [here](https://github.com/espressif/idf-extra-components/blob/master/esp_encrypted_img/README.md) for more details
|
||||
|
||||
|
||||
## How to use the example
|
||||
|
||||
To create self-signed certificate and key, refer to README.md in upper level 'examples' directory. This certificate should be flashed with binary as it will be used for connection with server.
|
||||
|
||||
### Creating RSA key for encryption
|
||||
|
||||
You can generate a public and private RSA key pair using following commands:
|
||||
|
||||
`openssl genrsa -out rsa_key/private.pem 3072`
|
||||
|
||||
This generates a 3072-bit RSA key pair, and writes them to a file.
|
||||
|
||||
Private key is required for decryption process and is used as input to the `esp_encrypted_img` component. Private key can either be embedded into the firmware or stored in NVS.
|
||||
|
||||
Encrypted image generation tool will derive public key (from private key) and use it for encryption purpose.
|
||||
|
||||
* **NOTE:** We highly recommend the use of flash encryption or NVS encryption to protect the RSA Private Key on the device.
|
||||
* **NOTE:** RSA key provided in the example is for demonstration purpose only. We recommend to create a new key for production applications.
|
||||
|
||||
## Build and Flash example
|
||||
|
||||
```
|
||||
idf.py build flash
|
||||
```
|
||||
|
||||
* An encrypted image is automatically generated by build system. Upload the generated encrypted image (`build/pre_encrypted_ota_secure.bin`) to a server for performing OTA update.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Refer the README.md in the parent directory for the setup details.
|
107
examples/system/ota/pre_encrypted_ota/example_test.py
Normal file
107
examples/system/ota/pre_encrypted_ota/example_test.py
Normal file
@ -0,0 +1,107 @@
|
||||
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import http.server
|
||||
import multiprocessing
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import ssl
|
||||
from typing import Any
|
||||
|
||||
import ttfw_idf
|
||||
from RangeHTTPServer import RangeRequestHandler
|
||||
from tiny_test_fw import DUT
|
||||
|
||||
server_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'server_certs/ca_cert.pem')
|
||||
key_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'server_certs/server_key.pem')
|
||||
enc_bin_name = 'pre_encrypted_ota_secure.bin'
|
||||
|
||||
|
||||
def get_my_ip() -> Any:
|
||||
s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s1.connect(('8.8.8.8', 80))
|
||||
my_ip = s1.getsockname()[0]
|
||||
s1.close()
|
||||
return my_ip
|
||||
|
||||
|
||||
def get_server_status(host_ip: Any, port: int) -> bool:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server_status = sock.connect_ex((host_ip, port))
|
||||
sock.close()
|
||||
return server_status == 0
|
||||
|
||||
|
||||
def https_request_handler(): # type: ignore
|
||||
"""
|
||||
Returns a request handler class that handles broken pipe exception
|
||||
"""
|
||||
class RequestHandler(RangeRequestHandler):
|
||||
def finish(self) -> None:
|
||||
try:
|
||||
if not self.wfile.closed:
|
||||
self.wfile.flush()
|
||||
self.wfile.close()
|
||||
except socket.error:
|
||||
pass
|
||||
self.rfile.close()
|
||||
|
||||
def handle(self) -> None:
|
||||
try:
|
||||
RangeRequestHandler.handle(self)
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
return RequestHandler
|
||||
|
||||
|
||||
def start_https_server(ota_image_dir: str, server_ip: Any, server_port: int) -> None:
|
||||
os.chdir(ota_image_dir)
|
||||
requestHandler = https_request_handler()
|
||||
httpd = http.server.HTTPServer((server_ip, server_port), requestHandler)
|
||||
|
||||
httpd.socket = ssl.wrap_socket(httpd.socket,
|
||||
keyfile=key_file,
|
||||
certfile=server_file, server_side=True)
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='EXAMPLE_ETH_OTA')
|
||||
def test_examples_protocol_pre_encrypted_ota_example(env, extra_data): # type: ignore
|
||||
dut1 = env.get_dut('pre_encrypted_ota_example', 'examples/system/ota/pre_encrypted_ota', dut_class=ttfw_idf.ESP32DUT)
|
||||
|
||||
server_port = 8001
|
||||
# start test
|
||||
host_ip = get_my_ip()
|
||||
if (get_server_status(host_ip, server_port) is False):
|
||||
thread1 = multiprocessing.Process(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
|
||||
thread1.daemon = True
|
||||
thread1.start()
|
||||
|
||||
artifacts = dut1.app.artifact_cls(dut1.app.idf_path,
|
||||
dut1.app.case_group.get_artifact_index_file(),
|
||||
dut1.app.app_path, dut1.app.config_name, dut1.app.target)
|
||||
artifacts.download_artifact_files(file_names=['pre_encrypted_ota_secure.bin'])
|
||||
|
||||
dut1.start_app()
|
||||
|
||||
dut1.expect('Loaded app from partition at offset', timeout=30)
|
||||
try:
|
||||
ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
thread1.terminate()
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
dut1.expect('Starting Pre Encrypted OTA example', timeout=30)
|
||||
|
||||
print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + enc_bin_name))
|
||||
dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + enc_bin_name)
|
||||
dut1.expect('Magic Verified', timeout=30)
|
||||
dut1.expect('Reading RSA private key', timeout=30)
|
||||
dut1.expect('upgrade successful. Rebooting', timeout=30)
|
||||
|
||||
thread1.terminate()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_protocol_pre_encrypted_ota_example()
|
@ -0,0 +1,8 @@
|
||||
idf_build_get_property(project_dir PROJECT_DIR)
|
||||
idf_component_register(SRCS "pre_encrypted_ota.c"
|
||||
INCLUDE_DIRS "."
|
||||
EMBED_TXTFILES ${project_dir}/server_certs/ca_cert.pem
|
||||
${project_dir}/rsa_key/private.pem)
|
||||
|
||||
create_esp_enc_img(${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin
|
||||
${project_dir}/rsa_key/private.pem ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}_secure.bin app)
|
45
examples/system/ota/pre_encrypted_ota/main/Kconfig.projbuild
Normal file
45
examples/system/ota/pre_encrypted_ota/main/Kconfig.projbuild
Normal file
@ -0,0 +1,45 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_FIRMWARE_UPGRADE_URL
|
||||
string "firmware upgrade url endpoint"
|
||||
default "https://192.168.0.3:8070/hello_world.bin"
|
||||
help
|
||||
URL of server which hosts the encrypted firmware image.
|
||||
|
||||
config EXAMPLE_FIRMWARE_UPGRADE_URL_FROM_STDIN
|
||||
bool
|
||||
default y if EXAMPLE_FIRMWARE_UPGRADE_URL = "FROM_STDIN"
|
||||
|
||||
config EXAMPLE_SKIP_COMMON_NAME_CHECK
|
||||
bool "Skip server certificate CN fieldcheck"
|
||||
default n
|
||||
help
|
||||
This allows you to skip the validation of OTA server certificate CN field.
|
||||
|
||||
config EXAMPLE_SKIP_VERSION_CHECK
|
||||
bool "Skip firmware version check"
|
||||
default n
|
||||
help
|
||||
This allows you to skip the firmware version check.
|
||||
|
||||
config EXAMPLE_OTA_RECV_TIMEOUT
|
||||
int "OTA Receive Timeout"
|
||||
default 5000
|
||||
help
|
||||
Maximum time for reception
|
||||
|
||||
config EXAMPLE_ENABLE_PARTIAL_HTTP_DOWNLOAD
|
||||
bool "Enable partial HTTP download"
|
||||
default n
|
||||
help
|
||||
This enables use of Range header in esp_https_ota component.
|
||||
Firmware image will be downloaded over multiple HTTP requests.
|
||||
|
||||
config EXAMPLE_HTTP_REQUEST_SIZE
|
||||
int "HTTP request size"
|
||||
default MBEDTLS_SSL_IN_CONTENT_LEN
|
||||
depends on EXAMPLE_ENABLE_PARTIAL_HTTP_DOWNLOAD
|
||||
help
|
||||
This options specifies HTTP request size. Number of bytes specified
|
||||
in this option will be downloaded in single HTTP request.
|
||||
endmenu
|
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
idf: ">=4.4"
|
||||
espressif/esp_encrypted_img: "^1.0.0"
|
188
examples/system/ota/pre_encrypted_ota/main/pre_encrypted_ota.c
Normal file
188
examples/system/ota/pre_encrypted_ota/main/pre_encrypted_ota.c
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/* Pre Encrypted HTTPS OTA example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_https_ota.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_encrypted_img.h"
|
||||
|
||||
#if CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
#include "esp_wifi.h"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "pre_encrypted_ota_example";
|
||||
extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
|
||||
extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
|
||||
|
||||
extern const uint8_t rsa_private_pem_start[] asm("_binary_private_pem_start");
|
||||
extern const uint8_t rsa_private_pem_end[] asm("_binary_private_pem_end");
|
||||
|
||||
#define OTA_URL_SIZE 256
|
||||
|
||||
static esp_decrypt_handle_t *ctx;
|
||||
|
||||
static esp_err_t _decrypt_cb(decrypt_cb_arg_t *args)
|
||||
{
|
||||
esp_err_t err;
|
||||
pre_enc_decrypt_arg_t pargs = {};
|
||||
pargs.data_in = (char *) args->data_in;
|
||||
pargs.data_in_len = args->data_in_len;
|
||||
err = esp_encrypted_img_decrypt_data(ctx, &pargs);
|
||||
if (err != ESP_OK && err != ESP_ERR_NOT_FINISHED) {
|
||||
return err;
|
||||
}
|
||||
if (pargs.data_out_len > 0) {
|
||||
args->data_out = pargs.data_out;
|
||||
args->data_out_len = pargs.data_out_len;
|
||||
} else {
|
||||
args->data_out_len = 0;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void pre_encrypted_ota_task(void *pvParameter)
|
||||
{
|
||||
ESP_LOGI(TAG, "Starting Pre Encrypted OTA example");
|
||||
|
||||
esp_err_t ota_finish_err = ESP_OK;
|
||||
esp_http_client_config_t config = {
|
||||
.url = CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL,
|
||||
.cert_pem = (char *)server_cert_pem_start,
|
||||
.timeout_ms = CONFIG_EXAMPLE_OTA_RECV_TIMEOUT,
|
||||
.keep_alive_enable = true,
|
||||
};
|
||||
esp_decrypt_cfg_t cfg = {};
|
||||
cfg.rsa_pub_key = (char *)rsa_private_pem_start;
|
||||
cfg.rsa_pub_key_len = rsa_private_pem_end - rsa_private_pem_start;
|
||||
ctx = esp_encrypted_img_decrypt_start(&cfg);
|
||||
if (ctx == NULL) {
|
||||
ESP_LOGE(TAG, "OTA upgrade failed");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL_FROM_STDIN
|
||||
char url_buf[OTA_URL_SIZE];
|
||||
if (strcmp(config.url, "FROM_STDIN") == 0) {
|
||||
example_configure_stdin_stdout();
|
||||
fgets(url_buf, OTA_URL_SIZE, stdin);
|
||||
int len = strlen(url_buf);
|
||||
url_buf[len - 1] = '\0';
|
||||
config.url = url_buf;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Configuration mismatch: wrong firmware upgrade image url");
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK
|
||||
config.skip_cert_common_name_check = true;
|
||||
#endif
|
||||
|
||||
esp_https_ota_config_t ota_config = {
|
||||
.http_config = &config,
|
||||
#ifdef CONFIG_EXAMPLE_ENABLE_PARTIAL_HTTP_DOWNLOAD
|
||||
.partial_http_download = true,
|
||||
.max_http_request_size = CONFIG_EXAMPLE_HTTP_REQUEST_SIZE,
|
||||
#endif
|
||||
.decrypt_cb = _decrypt_cb,
|
||||
};
|
||||
|
||||
esp_https_ota_handle_t https_ota_handle = NULL;
|
||||
esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ESP HTTPS OTA Begin failed");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
err = esp_https_ota_perform(https_ota_handle);
|
||||
if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
|
||||
break;
|
||||
}
|
||||
// esp_https_ota_perform returns after every read operation which gives user the ability to
|
||||
// monitor the status of OTA upgrade by calling esp_https_ota_get_image_len_read, which gives length of image
|
||||
// data read so far.
|
||||
ESP_LOGD(TAG, "Image bytes read: %d", esp_https_ota_get_image_len_read(https_ota_handle));
|
||||
}
|
||||
|
||||
if (esp_https_ota_is_complete_data_received(https_ota_handle) != true) {
|
||||
// the OTA image was not completely received and user can customise the response to this situation.
|
||||
ESP_LOGE(TAG, "Complete data was not received.");
|
||||
} else {
|
||||
err = esp_encrypted_img_decrypt_end(ctx);
|
||||
if (err != ESP_OK) {
|
||||
goto ota_end;
|
||||
}
|
||||
ota_finish_err = esp_https_ota_finish(https_ota_handle);
|
||||
if ((err == ESP_OK) && (ota_finish_err == ESP_OK)) {
|
||||
ESP_LOGI(TAG, "ESP_HTTPS_OTA upgrade successful. Rebooting ...");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
} else {
|
||||
if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||
ESP_LOGE(TAG, "Image validation failed, image is corrupted");
|
||||
}
|
||||
ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed 0x%x", ota_finish_err);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
ota_end:
|
||||
esp_https_ota_abort(https_ota_handle);
|
||||
ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Initialize NVS.
|
||||
esp_err_t err = nvs_flash_init();
|
||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
// 1.OTA app partition table has a smaller NVS partition size than the non-OTA
|
||||
// partition table. This size mismatch may cause NVS initialization to fail.
|
||||
// 2.NVS partition contains data in new format and cannot be recognized by this version of code.
|
||||
// If this happens, we erase NVS partition and initialize NVS again.
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
err = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( err );
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
#if CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
/* Ensure to disable any WiFi power save mode, this allows best throughput
|
||||
* and hence timings for overall OTA operation.
|
||||
*/
|
||||
esp_wifi_set_ps(WIFI_PS_NONE);
|
||||
#endif // CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
|
||||
xTaskCreate(&pre_encrypted_ota_task, "pre_encrypted_ota_task", 1024 * 8, NULL, 5, NULL);
|
||||
}
|
39
examples/system/ota/pre_encrypted_ota/rsa_key/private.pem
Normal file
39
examples/system/ota/pre_encrypted_ota/rsa_key/private.pem
Normal file
@ -0,0 +1,39 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
14
examples/system/ota/pre_encrypted_ota/sdkconfig.ci
Normal file
14
examples/system/ota/pre_encrypted_ota/sdkconfig.ci
Normal file
@ -0,0 +1,14 @@
|
||||
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_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_EXAMPLE_CONNECT_IPV6=y
|
6
examples/system/ota/pre_encrypted_ota/sdkconfig.defaults
Normal file
6
examples/system/ota/pre_encrypted_ota/sdkconfig.defaults
Normal file
@ -0,0 +1,6 @@
|
||||
# Default sdkconfig parameters to use the OTA
|
||||
# partition table layout, with a 4MB flash size
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_PARTITION_TABLE_TWO_OTA=y
|
||||
# Enable ESP HTTPS OTA decryption callback
|
||||
CONFIG_ESP_HTTPS_OTA_DECRYPT_CB=y
|
@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDWDCCAkACCQCbF4+gVh/MLjANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJJ
|
||||
TjELMAkGA1UECAwCTUgxDDAKBgNVBAcMA1BVTjEMMAoGA1UECgwDRVNQMQwwCgYD
|
||||
VQQLDANFU1AxDDAKBgNVBAMMA0VTUDEaMBgGCSqGSIb3DQEJARYLZXNwQGVzcC5j
|
||||
b20wHhcNMjEwNzEyMTIzNjI3WhcNNDEwNzA3MTIzNjI3WjBuMQswCQYDVQQGEwJJ
|
||||
TjELMAkGA1UECAwCTUgxDDAKBgNVBAcMA1BVTjEMMAoGA1UECgwDRVNQMQwwCgYD
|
||||
VQQLDANFU1AxDDAKBgNVBAMMA0VTUDEaMBgGCSqGSIb3DQEJARYLZXNwQGVzcC5j
|
||||
b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDhxF/y7bygndxPwiWL
|
||||
SwS9LY3uBMaJgup0ufNKVhx+FhGQOu44SghuJAaH3KkPUnt6SOM8jC97/yQuc32W
|
||||
ukI7eBZoA12kargSnzdv5m5rZZpd+NznSSpoDArOAONKVlzr25A1+aZbix2mKRbQ
|
||||
S5w9o1N2BriQuSzd8gL0Y0zEk3VkOWXEL+0yFUT144HnErnD+xnJtHe11yPO2fEz
|
||||
YaGiilh0ddL26PXTugXMZN/8fRVHP50P2OG0SvFpC7vghlLp4VFM1/r3UJnvL6Oz
|
||||
3ALc6dhxZEKQucqlpj8l1UegszQToopemtIj0qXTHw2+uUnkUyWIPjPC+wdOAoap
|
||||
rFTRAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAItw24y565k3C/zENZlxyzto44ud
|
||||
IYPQXN8Fa2pBlLe1zlSIyuaA/rWQ+i1daS8nPotkCbWZyf5N8DYaTE4B0OfvoUPk
|
||||
B5uGDmbuk6akvlB5BGiYLfQjWHRsK9/4xjtIqN1H58yf3QNROuKsPAeywWS3Fn32
|
||||
3//OpbWaClQePx6udRYMqAitKR+QxL7/BKZQsX+UyShuq8hjphvXvk0BW8ONzuw9
|
||||
RcoORxM0FzySYjeQvm4LhzC/P3ZBhEq0xs55aL2a76SJhq5hJy7T/Xz6NFByvlrN
|
||||
lFJJey33KFrAf5vnV9qcyWFIo7PYy2VsaaEjFeefr7q3sTFSMlJeadexW2Y=
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDhxF/y7bygndxP
|
||||
wiWLSwS9LY3uBMaJgup0ufNKVhx+FhGQOu44SghuJAaH3KkPUnt6SOM8jC97/yQu
|
||||
c32WukI7eBZoA12kargSnzdv5m5rZZpd+NznSSpoDArOAONKVlzr25A1+aZbix2m
|
||||
KRbQS5w9o1N2BriQuSzd8gL0Y0zEk3VkOWXEL+0yFUT144HnErnD+xnJtHe11yPO
|
||||
2fEzYaGiilh0ddL26PXTugXMZN/8fRVHP50P2OG0SvFpC7vghlLp4VFM1/r3UJnv
|
||||
L6Oz3ALc6dhxZEKQucqlpj8l1UegszQToopemtIj0qXTHw2+uUnkUyWIPjPC+wdO
|
||||
AoaprFTRAgMBAAECggEAE0HCxV/N1Q1h+1OeDDGL5+74yjKSFKyb/vTVcaPCrmaH
|
||||
fPvp0ddOvMZJ4FDMAsiQS6/n4gQ7EKKEnYmwTqj4eUYW8yxGUn3f0YbPHbZT+Mkj
|
||||
z5woi3nMKi/MxCGDQZX4Ow3xUQlITUqibsfWcFHis8c4mTqdh4qj7xJzehD2PVYF
|
||||
gNHZsvVj6MltjBDAVwV1IlGoHjuElm6vuzkfX7phxcA1B4ZqdYY17yCXUnvui46z
|
||||
Xn2kUTOOUCEgfgvGa9E+l4OtdXi5IxjaSraU+dlg2KsE4TpCuN2MEVkeR5Ms3Y7Q
|
||||
jgJl8vlNFJDQpbFukLcYwG7rO5N5dQ6WWfVia/5XgQKBgQD74at/bXAPrh9NxPmz
|
||||
i1oqCHMDoM9sz8xIMZLF9YVu3Jf8ux4xVpRSnNy5RU1gl7ZXbpdgeIQ4v04zy5aw
|
||||
8T4tu9K3XnR3UXOy25AK0q+cnnxZg3kFQm+PhtOCKEFjPHrgo2MUfnj+EDddod7N
|
||||
JQr9q5rEFbqHupFPpWlqCa3QmQKBgQDldWUGokNaEpmgHDMnHxiibXV5LQhzf8Rq
|
||||
gJIQXb7R9EsTSXEvsDyqTBb7PHp2Ko7rZ5YQfyf8OogGGjGElnPoU/a+Jij1gVFv
|
||||
kZ064uXAAISBkwHdcuobqc5EbG3ceyH46F+FBFhqM8KcbxJxx08objmh58+83InN
|
||||
P9Qr25Xw+QKBgEGXMHuMWgQbSZeM1aFFhoMvlBO7yogBTKb4Ecpu9wI5e3Kan3Al
|
||||
pZYltuyf+VhP6XG3IMBEYdoNJyYhu+nzyEdMg8CwXg+8LC7FMis/Ve+o7aS5scgG
|
||||
1to/N9DK/swCsdTRdzmc/ZDbVC+TuVsebFBGYZTyO5KgqLpezqaIQrTxAoGALFCU
|
||||
10glO9MVyl9H3clap5v+MQ3qcOv/EhaMnw6L2N6WVT481tnxjW4ujgzrFcE4YuxZ
|
||||
hgwYu9TOCmeqopGwBvGYWLbj+C4mfSahOAs0FfXDoYazuIIGBpuv03UhbpB1Si4O
|
||||
rJDfRnuCnVWyOTkl54gKJ2OusinhjztBjcrV1XkCgYEA3qNi4uBsPdyz9BZGb/3G
|
||||
rOMSw0CaT4pEMTLZqURmDP/0hxvTk1polP7O/FYwxVuJnBb6mzDa0xpLFPTpIAnJ
|
||||
YXB8xpXU69QVh+EBbemdJWOd+zp5UCfXvb2shAeG3Tn/Dz4cBBMEUutbzP+or0nG
|
||||
vSXnRLaxQhooWm+IuX9SuBQ=
|
||||
-----END PRIVATE KEY-----
|
@ -493,7 +493,6 @@ components/esp_hid/src/esp_hid_common.c
|
||||
components/esp_hid/src/esp_hidd.c
|
||||
components/esp_hid/test/hid_descriptor.h
|
||||
components/esp_hid/test/test_esp_hid.c
|
||||
components/esp_https_ota/include/esp_https_ota.h
|
||||
components/esp_hw_support/include/esp_clk.h
|
||||
components/esp_hw_support/include/soc/esp_himem.h
|
||||
components/esp_hw_support/include/soc/esp_spiram.h
|
||||
|
Loading…
Reference in New Issue
Block a user