OTA update with pre-encrypted file
Posted: Sat Mar 12, 2022 12:24 am
I needed to do encrypted OTA over BLE. I wanted to use an pre-encrypted file for the update. I have not found any information on this subject anywhere. And I know a lot of people ask about it. Here I will show you how to achieve ota with pre-generated keys and pre-encrypted file.
This is not a step by step tutorial. Just a proof of concept and a collection of information.
First of all, I will focus on OTA and not on the bluetooth connection.
Assumptions:
-flash encryption
-secure boot v2.
-pre-generated keys on PC
Goal:
-OTA update with an pre-encrypted file on the PC
1. We have to enable in sdk config secure boot v2 and flash encryption:
We need to resize the partition according to the tutorial.
https://docs.espressif.com/projects/esp ... ot-v2.html
-Activate secure boot and flash ecnryption
-It is important to disable Sign binaries during build
2. Generating keys, flashing by uart, file encryption and signing.
ESP code:
You need to add validations and other things to this code to safely upload. This is the minimum code that allows you to upload an encrypted file.
Check which partition esp32 is running from. Because the file for the ota0 and ota1 partitions are different. They are encrypted with a key shift.
If you flash ota0 partition please send the file app_name0.bin for ota1 send file app_name1.bin.
This is not a step by step tutorial. Just a proof of concept and a collection of information.
First of all, I will focus on OTA and not on the bluetooth connection.
Assumptions:
-flash encryption
-secure boot v2.
-pre-generated keys on PC
Goal:
-OTA update with an pre-encrypted file on the PC
1. We have to enable in sdk config secure boot v2 and flash encryption:
We need to resize the partition according to the tutorial.
https://docs.espressif.com/projects/esp ... ot-v2.html
-Activate secure boot and flash ecnryption
-It is important to disable Sign binaries during build
2. Generating keys, flashing by uart, file encryption and signing.
Code: Select all
import esptool
import espefuse
import espsecure
import os
import sys
import shutil
from pathlib import Path
bootloader_offset = 0x1000
partitions_offset = 0xF000
otadata_offset = 0x32000
phydata_offset = 0x31000
app_offset0 = 0x40000
app_offset1 = 0x240000
nvs_key_offset = 0x30000
nvs_key_length = 0x1000
nvs_data_offset = 0x10000
nvs_data_length = 0x20000
app_name = "myappname"
ota_data_name = "ota_data_initial"
partitions_name = "partition-table"
bootloader_name = "bootloader"
phydata_name = "phy_init_data"
esp = None
class Args(object):
pass
def genkeys(x):
Path("data/" + str(x) + "/keys").mkdir(parents=True, exist_ok=True)
if Path("data/" + str(x) + "/keys/signkey.pem").exists() == False:
args = Args()
args.keyfile = "data/" + str(x) + "/keys/signkey.pem"
args.version = "2"
espsecure.generate_signing_key(args)
else:
print("SIGNKEY EXIST")
if Path("data/" + str(x) + "/keys/flashkey.bin").exists() == False:
args = Args()
args.keylen = 256
args.key_file = open("data/" + str(x) + "/keys/flashkey.bin", 'wb')
espsecure.generate_flash_encryption_key(args)
else:
print("FLASHKEY EXIST")
def sign(x):
Path("temp/" + str(x)).mkdir(parents=True, exist_ok=True)
###################################################################################################################
args = Args()
args.keyfile = {open("data/" + str(x) + "/keys/signkey.pem", "rb")}
args.output = "temp/" + str(x) + "/signed_" + bootloader_name + ".bin"
args.datafile = open("../build/bootloader/" + bootloader_name + ".bin", "rb")
args.version = "2"
espsecure.sign_data(args)
###################################################################################################################
args = Args()
args.keyfile = {open("data/" + str(x) + "/keys/signkey.pem", "rb")}
args.output = "temp/" + str(x) + "/signed_" + partitions_name + ".bin"
args.datafile = open("../build/partition_table/" + partitions_name + ".bin", "rb")
args.version = "2"
args.append_signatures = True
espsecure.sign_data(args)
###################################################################################################################
args = Args()
args.keyfile = {open("data/" + str(x) + "/keys/signkey.pem", "rb")}
args.output = "temp/" + str(x) + "/signed_" + app_name + "0.bin"
args.datafile = open("../build/" + app_name + ".bin", "rb")
args.version = "2"
args.append_signatures = True
espsecure.sign_data(args)
###################################################################################################################
args = Args()
args.keyfile = {open("data/" + str(x) + "/keys/signkey.pem", "rb")}
args.output = "temp/" + str(x) + "/signed_" + app_name + "1.bin"
args.datafile = open("../build/" + app_name + ".bin", "rb")
args.version = "2"
args.append_signatures = True
espsecure.sign_data(args)
###################################################################################################################
def encrypt(x):
Path("update/" + str(x)).mkdir(parents=True, exist_ok=True)
###################################################################################################################
shutil.copyfile("../build/" + phydata_name + ".bin", "update/" + str(x) + "/no_crypt_" + phydata_name + ".bin")
###################################################################################################################
args = Args()
args.keyfile = open("data/" + str(x) + "/keys/flashkey.bin", "rb")
args.output = open("update/" + str(x) + "/crypt_" + bootloader_name + ".bin", "wb")
args.plaintext_file = open("temp/" + str(x) + "/signed_" + bootloader_name + ".bin", "rb")
args.aes_xts = False
args.flash_crypt_conf = 15
args.address = bootloader_offset
espsecure.encrypt_flash_data(args)
args.plaintext_file.close()
os.remove(args.plaintext_file.name)
###################################################################################################################
args = Args()
args.keyfile = open("data/" + str(x) + "/keys/flashkey.bin", "rb")
args.output = open("update/" + str(x) + "/crypt_" + partitions_name + ".bin", "wb")
args.plaintext_file = open("temp/" + str(x) + "/signed_" + partitions_name + ".bin", "rb")
args.aes_xts = False
args.flash_crypt_conf = 15
args.address = partitions_offset
espsecure.encrypt_flash_data(args)
args.plaintext_file.close()
os.remove(args.plaintext_file.name)
###################################################################################################################
args = Args()
args.keyfile = open("data/" + str(x) + "/keys/flashkey.bin", "rb")
args.output = open("update/" + str(x) + "/crypt_" + ota_data_name + ".bin", "wb")
args.plaintext_file = open("../build/" + ota_data_name + ".bin", "rb")
args.aes_xts = False
args.flash_crypt_conf = 15
args.address = otadata_offset
espsecure.encrypt_flash_data(args)
args.plaintext_file.close()
###################################################################################################################
args = Args()
args.keyfile = open("data/" + str(x) + "/keys/flashkey.bin", "rb")
args.output = open("update/" + str(x) + "/crypt_" + app_name + "0.bin", "wb")
args.plaintext_file = open("temp/" + str(x) + "/signed_" + app_name + "0.bin", "rb")
args.aes_xts = False
args.flash_crypt_conf = 15
args.address = app_offset0
espsecure.encrypt_flash_data(args)
args.plaintext_file.close()
os.remove(args.plaintext_file.name)
###################################################################################################################
args = Args()
args.keyfile = open("data/" + str(x) + "/keys/flashkey.bin", "rb")
args.output = open("update/" + str(x) + "/crypt_" + app_name + "1.bin", "wb")
args.plaintext_file = open("temp/" + str(x) + "/signed_" + app_name + "1.bin", "rb")
args.aes_xts = False
args.flash_crypt_conf = 15
args.address = app_offset1
espsecure.encrypt_flash_data(args)
args.plaintext_file.close()
os.remove(args.plaintext_file.name)
###################################################################################################################
os.rmdir("temp/" + str(x))
os.rmdir("temp")
def flash():
if len(sys.argv) > 1:
port = sys.argv[1]
else:
port = "COM8"
print("Serial port %s" % port)
chip = "esp32"
initial_baud = 115200
trace = False
before = 'default_reset'
connect_attempts = 15
chip_class = esptool._chip_to_rom_loader(chip)
esp = chip_class(port, initial_baud, trace)
esp.connect(before, connect_attempts)
esp = esp.run_stub()
esp.change_baud(921600)
mac_addr = ':'.join(map('{:02x}'.format, esp.read_mac())).upper()
print(mac_addr)
x = mac_addr.replace(":", "")
genkeys(x)
sign(x)
encrypt(x)
file = open("data/" + str(x) + "/mac.txt", "w+")
file.write(mac_addr)
file.close()
efuses, efuse_operations = espefuse.get_efuses(esp, do_not_confirm=True)
if efuses['BLOCK1'].is_writeable():
args = Args()
args.block = ['flash_encryption']
args.keyfile = [open("data/" + str(x) + "/keys/flashkey.bin", "rb")]
args.force_write_always = 0
args.no_protect_key = 0
args.only_burn_at_end = False
efuse_operations.burn_key(esp, efuses, args)
if efuses['BLOCK2'].is_writeable():
args = Args()
args.keyfile = open("data/" + str(x) + "/keys/signkey.pem", "rb")
args.no_protect_key = 0
args.only_burn_at_end = False
efuse_operations.burn_key_digest(esp, efuses, args)
args = Args()
args.name_value_pairs = {'FLASH_CRYPT_CNT': 1,
'DISABLE_DL_ENCRYPT': 1, 'DISABLE_DL_DECRYPT': 1, 'DISABLE_DL_CACHE': 1,
'JTAG_DISABLE': 1,
'ABS_DONE_1': 1, 'FLASH_CRYPT_CONFIG': 0xf}
args.only_burn_at_end = False
efuse_operations.burn_efuse(esp, efuses, args)
args = Args()
args.efuse_name = ['MAC', 'FLASH_CRYPT_CNT', 'DISABLE_DL_ENCRYPT', 'DISABLE_DL_DECRYPT', 'DISABLE_DL_CACHE',
'JTAG_DISABLE', 'ABS_DONE_1', 'FLASH_CRYPT_CONFIG', 'ABS_DONE_0', 'BLOCK1', 'BLOCK2']
args.only_burn_at_end = False
efuse_operations.write_protect_efuse(esp, efuses, args)
args = Args()
args.address = nvs_key_offset
args.size = nvs_key_length
esptool.erase_region(esp, args)
args = Args()
args.address = nvs_data_offset
args.size = nvs_data_length
esptool.erase_region(esp, args)
args = Args()
args.compress = True
args.no_compress = False
args.no_stub = False
args.erase_all = False
args.encrypt = False
args.verify = False
args.encrypt_files = None
args.flash_size = 'keep'
args.flash_mode = 'keep'
args.flash_freq = 'keep'
args.addr_filename = [
(bootloader_offset, open("update/" + str(x) + "/crypt_" + bootloader_name + ".bin", "rb")),
(partitions_offset, open("update/" + str(x) + "/crypt_" + partitions_name + ".bin", "rb")),
(otadata_offset, open("update/" + str(x) + "/crypt_" + ota_data_name + ".bin", "rb")),
(phydata_offset, open("update/" + str(x) + "/no_crypt_" + phydata_name + ".bin", "rb")),
(app_offset0, open("update/" + str(x) + "/crypt_" + app_name + "0.bin", "rb"))]
esptool.write_flash(esp, args)
args = Args()
args.format = 'summary'
args.file = sys.stdout
efuse_operations.summary(esp, efuses, args)
esp.hard_reset()
esp._port.close()
Code: Select all
class OTAUpdate {
public:
bool begin(size_t size);
esp_err_t write(uint8_t *data, size_t len, size_t offset);
bool end();
bool reboot();
const esp_partition_t* _partition;
};
Code: Select all
bool OTAUpdate::begin(size_t size) {
_partition = esp_ota_get_next_update_partition(NULL);
esp_err_t a = esp_partition_erase_range(_partition, 0, 0x200000);
if(!_partition){
Serial.println("NO PARTITION TO OTA");
return false;
} else {
Serial.println("OTA Partition");
Serial.println(_partition->label);
}
if(size > _partition->size) {
Serial.println("PARTITION TO SMALL");
return false;
} else {
Serial.println("PARTITION SIZE OK");
}
return true;
}
esp_err_t OTAUpdate::write(uint8_t *data, size_t len, size_t offset) {
esp_err_t b = esp_partition_write_raw(_partition, offset, data, len);
return b;
}
bool OTAUpdate::end() {
esp_ota_set_boot_partition(_partition);
return true;
}
bool OTAUpdate::reboot() {
ESP.restart();
return true;
}
Check which partition esp32 is running from. Because the file for the ota0 and ota1 partitions are different. They are encrypted with a key shift.
Code: Select all
const esp_partition_t* partition = esp_ota_get_running_partition();