Skip to content

Commit 4478256

Browse files
shubhamdppull[bot]
authored andcommitted
[ESP32] Support for using encrypted app binary for OTA upgrades (#26978)
* [ESP32] Link wpa_supplicant library to libchip * [ESP32] Support for using encrypted app binary for OTA upgrades * Changes in application code * Guide for how to use encrypted ota * remove ota configs from sdkconfig.defaults * Added few words to wordlist * changed keypair to "key pair"
1 parent ef99bc0 commit 4478256

File tree

9 files changed

+236
-2
lines changed

9 files changed

+236
-2
lines changed

.github/.wordlist.txt

+3
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,8 @@ DCONF
357357
DCONFIG
358358
debianutils
359359
debugText
360+
decrypt
361+
decrypted
360362
DEDEDEDE
361363
deepnote
362364
DefaultOTAProviders
@@ -1151,6 +1153,7 @@ RPC
11511153
RPCs
11521154
RPi
11531155
RPis
1156+
RSA
11541157
rsn
11551158
RSSI
11561159
RST

config/esp32/components/chip/CMakeLists.txt

+8
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,11 @@ if((NOT CONFIG_USE_MINIMAL_MDNS) AND (CONFIG_ENABLE_WIFI_STATION OR CONFIG_ENABL
388388
list(APPEND chip_libraries $<TARGET_FILE:${mdns_lib}>)
389389
endif()
390390

391+
if (CONFIG_ENABLE_ENCRYPTED_OTA)
392+
idf_component_get_property(esp_encrypted_img_lib espressif__esp_encrypted_img COMPONENT_LIB)
393+
list(APPEND chip_libraries $<TARGET_FILE:${esp_encrypted_img_lib}>)
394+
endif()
395+
391396
idf_component_get_property(main_lib main COMPONENT_LIB)
392397
list(APPEND chip_libraries $<TARGET_FILE:${main_lib}>)
393398

@@ -413,6 +418,9 @@ if (CONFIG_ESP32_WIFI_ENABLED)
413418
foreach(blob ${blobs})
414419
list(APPEND chip_libraries "${esp_wifi_dir}/lib/${target_name}/lib${blob}.a")
415420
endforeach()
421+
422+
idf_component_get_property(wpa_supplicant_lib wpa_supplicant COMPONENT_LIB)
423+
list(APPEND chip_libraries $<TARGET_FILE:${wpa_supplicant_lib}>)
416424
endif()
417425

418426
idf_component_get_property(esp_netif_lib esp_netif COMPONENT_LIB)

config/esp32/components/chip/Kconfig

+7
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,13 @@ menu "CHIP Core"
190190
help
191191
Enable this option to enable OTA Requestor for example
192192

193+
config ENABLE_ENCRYPTED_OTA
194+
bool "Enable pre encrypted OTA"
195+
depends on ENABLE_OTA_REQUESTOR
196+
default n
197+
help
198+
Enable this option to use the pre encrypted OTA image
199+
193200
config OTA_AUTO_REBOOT_ON_APPLY
194201
bool "Reboot the device after applying the OTA image"
195202
depends on ENABLE_OTA_REQUESTOR

config/esp32/components/chip/idf_component.yml

+5
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,8 @@ dependencies:
1010
version: "^2.3.0"
1111
rules:
1212
- if: "idf_version >=4.3"
13+
14+
espressif/esp_encrypted_img:
15+
version: "2.0.3"
16+
rules:
17+
- if: "idf_version >=4.4"

docs/guides/esp32/ota.md

+54
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,57 @@ chip-tool. On receiving this command OTA requestor will query for OTA image.
6767
```
6868
./out/debug/chip-tool otasoftwareupdaterequestor announce-ota-provider <PROVIDER NODE ID> 0 0 0 <REQUESTOR NODE ID> 0
6969
```
70+
71+
## Encrypted OTA
72+
73+
ESP32 supports transferring encrypted OTA images. Currently, an application
74+
image can be encrypted/decrypted using an RSA-3072 key pair.
75+
76+
### Firmware Changes
77+
78+
- Enable configuration options for OTA requestor and Encrypted OTA:
79+
80+
```
81+
CONFIG_ENABLE_OTA_REQUESTOR=y
82+
CONFIG_ENABLE_ENCRYPTED_OTA=y
83+
```
84+
85+
- Applications need to provide the key pair to the OTA image processor using
86+
the `InitEncryptedOTA()` API to decrypt the received OTA image.
87+
88+
- For testing purposes, in `examples/lighting-app/esp32`, there is a logic of
89+
embedding the private key in the firmware. To quickly test, please generate
90+
the key pair and rename it as `esp_image_encryption_public_key.pem` and copy
91+
it to directory `examples/lighting-app/esp32`.
92+
93+
Please follow the steps below to generate an application image for OTA upgrades:
94+
95+
1. Generate a new RSA-3072 key pair or use an existing one.
96+
97+
- To generate a key pair, use the following command:
98+
99+
```
100+
openssl genrsa -out esp_image_encryption_key.pem 3072
101+
```
102+
103+
- Extract the public key from the key pair:
104+
```
105+
openssl rsa -in esp_image_encryption_key.pem -pubout -out esp_image_encryption_public_key.pem
106+
```
107+
108+
2. Encrypt the application binary using the
109+
[esp_enc_img_gen.py](https://github.com/espressif/idf-extra-components/blob/master/esp_encrypted_img/tools/esp_enc_img_gen.py)
110+
script.
111+
112+
- Use the following command to encrypt the OTA image with the public key:
113+
114+
```
115+
python3 esp_enc_img_gen.py encrypt lighting-app.bin esp_image_encryption_public_key.pem lighting-app-encrypted.bin
116+
```
117+
118+
- Append the Matter OTA header:
119+
```
120+
src/app/ota_image_tool.py create --vendor-id 0xFFF1 --product-id 0x8000 --version 2 --version-str "v2.0" -da sha256 lighting-app-encrypted.bin lighting-app-encrypted-ota.bin
121+
```
122+
123+
3. Use the `lighting-app-encrypted-ota.bin` file with the OTA Provider app.

examples/lighting-app/esp32/CMakeLists.txt

+6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ endif()
3737

3838
project(chip-lighting-app)
3939

40+
# WARNING: This is just an example for using key for decrypting the encrypted OTA image
41+
# Please do not use it as is.
42+
if(CONFIG_ENABLE_ENCRYPTED_OTA)
43+
target_add_binary_data(chip-lighting-app.elf "esp_image_encryption_key.pem" TEXT)
44+
endif()
45+
4046
# C++17 is required for RPC build.
4147
idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++17;-Os;-DCHIP_HAVE_CONFIG_H" APPEND)
4248
idf_build_set_property(C_COMPILE_OPTIONS "-Os" APPEND)

examples/platform/esp32/ota/OTAHelper.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ chip::Optional<bool> gRequestorCanConsent;
4949
static chip::ota::UserConsentState gUserConsentState = chip::ota::UserConsentState::kUnknown;
5050
chip::ota::DefaultOTARequestorUserConsent gUserConsentProvider;
5151

52+
// WARNING: This is just an example for using key for decrypting the encrypted OTA image
53+
// Please do not use it as is for production use cases
54+
#if CONFIG_ENABLE_ENCRYPTED_OTA
55+
extern const char sOTADecryptionKeyStart[] asm("_binary_esp_image_encryption_key_pem_start");
56+
extern const char sOTADecryptionKeyEnd[] asm("_binary_esp_image_encryption_key_pem_end");
57+
58+
CharSpan sOTADecryptionKey(sOTADecryptionKeyStart, sOTADecryptionKeyEnd - sOTADecryptionKeyStart);
59+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
60+
5261
} // namespace
5362

5463
bool CustomOTARequestorDriver::CanConsent()
@@ -67,6 +76,10 @@ void OTAHelpers::InitOTARequestor()
6776
gDownloader.SetImageProcessorDelegate(&gImageProcessor);
6877
gRequestorUser.Init(&gRequestorCore, &gImageProcessor);
6978

79+
#if CONFIG_ENABLE_ENCRYPTED_OTA
80+
gImageProcessor.InitEncryptedOTA(sOTADecryptionKey);
81+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
82+
7083
if (gUserConsentState != chip::ota::UserConsentState::kUnknown)
7184
{
7285
gUserConsentProvider.SetUserConsentState(gUserConsentState);

src/platform/ESP32/OTAImageProcessorImpl.cpp

+121-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
#include "esp_system.h"
2828
#include "lib/core/CHIPError.h"
2929

30+
#if CONFIG_ENABLE_ENCRYPTED_OTA
31+
#include <esp_encrypted_img.h>
32+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
33+
3034
#define TAG "OTAImageProcessor"
3135
using namespace chip::System;
3236
using namespace ::chip::DeviceLayer::Internal;
@@ -54,6 +58,20 @@ void PostOTAStateChangeEvent(DeviceLayer::OtaState newState)
5458

5559
} // namespace
5660

61+
#if CONFIG_ENABLE_ENCRYPTED_OTA
62+
void OTAImageProcessorImpl::EndDecryption()
63+
{
64+
VerifyOrReturn(mEncryptedOTAEnabled);
65+
66+
esp_err_t err = esp_encrypted_img_decrypt_end(mOTADecryptionHandle);
67+
if (err != ESP_OK)
68+
{
69+
ChipLogError(SoftwareUpdate, "Failed to end pre encrypted OTA esp_err:%d", err);
70+
}
71+
mOTADecryptionHandle = nullptr;
72+
}
73+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
74+
5775
bool OTAImageProcessorImpl::IsFirstImageRun()
5876
{
5977
OTARequestorInterface * requestor = GetRequestorInstance();
@@ -145,6 +163,32 @@ void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context)
145163
imageProcessor->mDownloader->OnPreparedForDownload(ESP32Utils::MapError(err));
146164
return;
147165
}
166+
167+
#if CONFIG_ENABLE_ENCRYPTED_OTA
168+
if (imageProcessor->mEncryptedOTAEnabled == false)
169+
{
170+
ChipLogError(SoftwareUpdate, "Encrypted OTA is not initialized");
171+
imageProcessor->mDownloader->OnPreparedForDownload(ESP32Utils::MapError(err));
172+
return;
173+
}
174+
175+
// This struct takes in private key but arguments are named as pub_key
176+
// This is the issue in the esp_encrypted_img component
177+
// https://github.com/espressif/idf-extra-components/blob/791d506/esp_encrypted_img/include/esp_encrypted_img.h#L47
178+
const esp_decrypt_cfg_t decryptionConfig = {
179+
.rsa_pub_key = imageProcessor->mKey.data(),
180+
.rsa_pub_key_len = imageProcessor->mKey.size(),
181+
};
182+
183+
imageProcessor->mOTADecryptionHandle = esp_encrypted_img_decrypt_start(&decryptionConfig);
184+
if (imageProcessor->mOTADecryptionHandle == nullptr)
185+
{
186+
ChipLogError(SoftwareUpdate, "Failed to initialize encrypted OTA");
187+
imageProcessor->mDownloader->OnPreparedForDownload(ESP32Utils::MapError(ESP_FAIL));
188+
return;
189+
}
190+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
191+
148192
imageProcessor->mHeaderParser.Init();
149193
imageProcessor->mDownloader->OnPreparedForDownload(CHIP_NO_ERROR);
150194
PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadInProgress);
@@ -158,6 +202,11 @@ void OTAImageProcessorImpl::HandleFinalize(intptr_t context)
158202
ChipLogError(SoftwareUpdate, "ImageProcessor context is null");
159203
return;
160204
}
205+
206+
#if CONFIG_ENABLE_ENCRYPTED_OTA
207+
imageProcessor->EndDecryption();
208+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
209+
161210
esp_err_t err = esp_ota_end(imageProcessor->mOTAUpdateHandle);
162211
if (err != ESP_OK)
163212
{
@@ -185,6 +234,11 @@ void OTAImageProcessorImpl::HandleAbort(intptr_t context)
185234
ChipLogError(SoftwareUpdate, "ImageProcessor context is null");
186235
return;
187236
}
237+
238+
#if CONFIG_ENABLE_ENCRYPTED_OTA
239+
imageProcessor->EndDecryption();
240+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
241+
188242
if (esp_ota_abort(imageProcessor->mOTAUpdateHandle) != ESP_OK)
189243
{
190244
ESP_LOGE(TAG, "ESP OTA abort failed");
@@ -218,15 +272,68 @@ void OTAImageProcessorImpl::HandleProcessBlock(intptr_t context)
218272
return;
219273
}
220274

221-
esp_err_t err = esp_ota_write(imageProcessor->mOTAUpdateHandle, block.data(), block.size());
275+
esp_err_t err;
276+
ByteSpan blockToWrite = block;
277+
278+
#if CONFIG_ENABLE_ENCRYPTED_OTA
279+
if (imageProcessor->mEncryptedOTAEnabled == false)
280+
{
281+
ChipLogError(SoftwareUpdate, "Encrypted OTA is not initialized");
282+
imageProcessor->mDownloader->EndDownload(CHIP_ERROR_INCORRECT_STATE);
283+
PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadFailed);
284+
return;
285+
}
286+
287+
if (imageProcessor->mOTADecryptionHandle == nullptr)
288+
{
289+
ChipLogError(SoftwareUpdate, "OTA decryption handle is nullptr");
290+
imageProcessor->mDownloader->EndDownload(CHIP_ERROR_INCORRECT_STATE);
291+
PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadFailed);
292+
return;
293+
}
294+
295+
pre_enc_decrypt_arg_t preEncOtaDecryptArgs = {
296+
.data_in = reinterpret_cast<const char *>(block.data()),
297+
.data_in_len = block.size(),
298+
.data_out = nullptr,
299+
.data_out_len = 0,
300+
};
301+
302+
err = esp_encrypted_img_decrypt_data(imageProcessor->mOTADecryptionHandle, &preEncOtaDecryptArgs);
303+
if (err != ESP_OK && err != ESP_ERR_NOT_FINISHED)
304+
{
305+
ChipLogError(SoftwareUpdate, "esp_encrypted_img_decrypt_data failed err:%d", err);
306+
imageProcessor->mDownloader->EndDownload(CHIP_ERROR_WRITE_FAILED);
307+
PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadFailed);
308+
return;
309+
}
310+
311+
ChipLogDetail(SoftwareUpdate, "data_in_len:%u, data_out_len:%u", preEncOtaDecryptArgs.data_in_len,
312+
preEncOtaDecryptArgs.data_out_len);
313+
314+
if (preEncOtaDecryptArgs.data_out == nullptr || preEncOtaDecryptArgs.data_out_len <= 0)
315+
{
316+
ChipLogProgress(SoftwareUpdate, "Decrypted data is null or out len is zero");
317+
}
318+
319+
blockToWrite = ByteSpan(reinterpret_cast<const uint8_t *>(preEncOtaDecryptArgs.data_out), preEncOtaDecryptArgs.data_out_len);
320+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
321+
322+
err = esp_ota_write(imageProcessor->mOTAUpdateHandle, blockToWrite.data(), blockToWrite.size());
323+
324+
#if CONFIG_ENABLE_ENCRYPTED_OTA
325+
free(preEncOtaDecryptArgs.data_out);
326+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
327+
222328
if (err != ESP_OK)
223329
{
224330
ESP_LOGE(TAG, "esp_ota_write failed (%s)", esp_err_to_name(err));
225331
imageProcessor->mDownloader->EndDownload(CHIP_ERROR_WRITE_FAILED);
226332
PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadFailed);
227333
return;
228334
}
229-
imageProcessor->mParams.downloadedBytes += block.size();
335+
336+
imageProcessor->mParams.downloadedBytes += blockToWrite.size();
230337
imageProcessor->mDownloader->FetchNextData();
231338
}
232339

@@ -310,4 +417,16 @@ CHIP_ERROR OTAImageProcessorImpl::ProcessHeader(ByteSpan & block)
310417
return CHIP_NO_ERROR;
311418
}
312419

420+
#if CONFIG_ENABLE_ENCRYPTED_OTA
421+
CHIP_ERROR OTAImageProcessorImpl::InitEncryptedOTA(const CharSpan & key)
422+
{
423+
VerifyOrReturnError(mEncryptedOTAEnabled == false, CHIP_ERROR_INCORRECT_STATE);
424+
VerifyOrReturnError(IsSpanUsable(key), CHIP_ERROR_INVALID_ARGUMENT);
425+
426+
mKey = key;
427+
mEncryptedOTAEnabled = true;
428+
return CHIP_NO_ERROR;
429+
}
430+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
431+
313432
} // namespace chip

src/platform/ESP32/OTAImageProcessorImpl.h

+19
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
#include <platform/CHIPDeviceLayer.h>
2424
#include <platform/OTAImageProcessor.h>
2525

26+
#if CONFIG_ENABLE_ENCRYPTED_OTA
27+
#include <esp_encrypted_img.h>
28+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
29+
2630
namespace chip {
2731

2832
class OTAImageProcessorImpl : public OTAImageProcessorInterface
@@ -38,6 +42,13 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
3842
bool IsFirstImageRun() override;
3943
CHIP_ERROR ConfirmCurrentImage() override;
4044

45+
#if CONFIG_ENABLE_ENCRYPTED_OTA
46+
// @brief This API initializes the handling of encrypted OTA image
47+
// @param key null terminated RSA-3072 key in PEM format
48+
// @return CHIP_NO_ERROR on success, appropriate error code otherwise
49+
CHIP_ERROR InitEncryptedOTA(const CharSpan & key);
50+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
51+
4152
private:
4253
static void HandlePrepareDownload(intptr_t context);
4354
static void HandleFinalize(intptr_t context);
@@ -54,6 +65,14 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
5465
const esp_partition_t * mOTAUpdatePartition = nullptr;
5566
esp_ota_handle_t mOTAUpdateHandle;
5667
OTAImageHeaderParser mHeaderParser;
68+
69+
#if CONFIG_ENABLE_ENCRYPTED_OTA
70+
void EndDecryption();
71+
72+
CharSpan mKey;
73+
bool mEncryptedOTAEnabled = false;
74+
esp_decrypt_handle_t mOTADecryptionHandle = nullptr;
75+
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
5776
};
5877

5978
} // namespace chip

0 commit comments

Comments
 (0)