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