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:
Mahavir Jain 2022-02-17 13:36:54 +00:00
commit 69f51a989c
19 changed files with 745 additions and 75 deletions

View File

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

View File

@ -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
*/

View 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

View File

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

View File

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

View File

@ -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()

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

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

View 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()

View File

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

View 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

View File

@ -0,0 +1,3 @@
dependencies:
idf: ">=4.4"
espressif/esp_encrypted_img: "^1.0.0"

View 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);
}

View File

@ -0,0 +1,39 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View 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

View 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

View File

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

View File

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

View File

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