Skip to main content


Compilando o firmware com Secure Boot e Flash Encryption​


Não vou ficar perdendo tempo explicando sobre o Flash Encryption porque eu já expliquei, se quiser entender, leia o tutorial anterior na parte de "Preciso ativar Flash Encryption?".


Nesse procedimento, a ideia é gerar a chave de Flash Encryption no computador, manter uma cópia dela em segurança e criptografar os binários antes de gravar eles usando o ROM Download. Para isso funcionar, a mesma chave também precisa estar gravada e protegida em um bloco eFuse do ESP32-S3. O hardware usa essa chave para descriptografar o conteúdo durante a execução.


Assim como no tutorial anterior, vou usar o ESP-IDF 5.5.1, que é usado pelo Projeto do PicoKeys. Os projetos foram compilados usando o commits abaixo:

### FIDO
Hash: ade770281dd4fd8c627add34ed078f56ec40a07b
tag: nightly-main e nightly-development

### HSM
Hash: 5d6a32c81b2367fb83e5320400f61217d2bd0d2f
tag: nightly-master e nightly-development

Atenção

A gravação de eFuses é permanente e irreversível. Uma configuração incorreta pode impedir a inicialização da placa, impossibilitar futuras atualizações ou tornar ela inutilizável.


Não me responsabilizo por quaisquer danos, falhas ou perdas decorrentes da execução dos procedimentos descritos neste guia, incluindo problemas causados pela compilação, configuração ou gravação do firmware.


Antes de realizar qualquer procedimento, recomendo a leitura da documentação oficial dos fabricantes e dos projetos envolvidos. Cada usuário é responsável por compreender os riscos e verificar a compatibilidade das instruções com seu dispositivo.



Clonando os projetos e instalando o ESP-IDF 5.5.1​


No Ubuntu, instale as dependências indicadas pelo ESP-IDF. Depois faça os clones:

Terminal
# Crie um diretório novo para esse projeto:
mkdir -p ~/src/picokeys-fe && cd ~/src/picokeys-fe

# Clone o ESP-IDF na versão 5.5.1:
git clone --branch v5.5.1 --recursive https://github.com/espressif/esp-idf.git esp-idf

# Clone o Pico FIDO:
git clone --recursive https://github.com/polhenarejos/pico-fido.git

# Clone o Pico HSM:
git clone --recursive https://github.com/polhenarejos/pico-hsm.git

Agora intale o esp-idf e carregue o ambiente:

Terminal
# Entre no diretório do projeto esp-idf:
cd esp-idf

# Instalar o esp-idf pra esp32-s3:
unset IDF_PYTHON_ENV_PATH ; ./install.sh esp32s3

# Carregue o ambiente ESP-IDF:
. ./export.sh

# Verifique a versão:
$ idf.py --version
ESP-IDF v5.5.1

# Volte para o diretório base:
cd ~/src/picokeys-fe


Ajustes necessários para HSM e FIDO​


Assim como descrito nos outros procedimentos, temos que fazer os ajustes necessários tanto no FIDO quanto no PICO para corrigir os problemas relacionado a segurança da chave e qualquer outro ajuste que seja necessário.


Não vou desmontrar o que precisa ser feito, para não replicar o mesmo processo que já foi tutorial anterior nesse aqui.



Gerando as chaves​


Para usar Secure Boot V2, precisamos de uma chave privada RSA-3072. Ela será usada para assinar o bootloader e a aplicação. O ESP32-S3 guarda nos eFuses o digest da chave pública correspondente.


A chave de Flash Encryption é outra chave, ela usa AES-XTS e será usada para criptografar os binários no computador. Essa chave também precisa ser provisionada no ESP32-S3 em um bloco eFuse com a finalidade XTS_AES_128_KEY.


Vamos começar criando os diretórios e restringindo as permissões:

Terminal
mkdir -p ~/.config/picokeys/signing
mkdir -p ~/.config/picokeys/flash-encryption
chmod 700 ~/.config/picokeys/signing ~/.config/picokeys/flash-encryption
umask 077

Agora gere a chave de assinatura (Secure Boot):

Terminal
espsecure.py generate_signing_key --version 2 --scheme rsa3072 \
~/.config/picokeys/signing/secure-boot.pem

chmod 600 ~/.config/picokeys/signing/secure-boot.pem

Vamos obter o MAC da placa, esse MAC será apenas um nome, um identificador que poderia ser qualquer coisa. Vou usar ele para facilitar identificar a qual placa pertence essa chave. Habilite o Download Mode (Segure BOOT, pressione e solte RESET e então solte BOOT).


Depois execute o comando abaixo para ver em qual porta ele subiu:

Terminal
$ ls -l /dev/ttyACM* /dev/ttyUSB*
/dev/ttyACM0

No meu caso foi em /dev/ttyACM0, vou usar esse nome no comando abaixo para gravar o firmware. Se o use foi outro, use o nome que apareceu ai, mas certifique-se de que é o ESP32-S3 correto que você quer gravar. Agora vamos ver o MAC da placa:

Terminal
$ esptool.py --chip esp32s3 --port /dev/ttyACM0 read_mac
esptool.py v4.12.0
Serial port /dev/ttyACM0
Connecting...
Chip is ESP32-S3 (QFN56) (revision v0.2)
Features: WiFi, BLE, Embedded PSRAM 8MB (AP_3v3)
Crystal is 40MHz
USB mode: USB-Serial/JTAG
MAC: 68:ee:8f:51:e0:38
Uploading stub...
Running stub...
Stub running...
MAC: 68:ee:8f:51:e0:38
Hard resetting via RTS pin...

Agora que temos o MAC, vamos usar ele como ID (68ee8f51e038). O comando abaixo vai gerar uma chave de 256 bits (usada para Flash Encryption):

Terminal
espsecure.py generate_flash_encryption_key \
~/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin

chmod 600 ~/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin

Troque 68ee8f51e038 por um identificador seu. Use uma chave FE diferente para cada dispositivo e mantenha uma cópia de segurança protegida. Se perder essa chave, você não vai mais conseguir usar essa placa.


Para salvar esse arquivo em algum lugar seguro, como um cofre de senhas, podemos fazer assim:

Terminal
xxd -p -c 256 ~/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin

Você vai ver um hash de 256 bits:

11c860d531eec46bb9b29319a798da103e7c121c7dcbe9aa2b251a5a855daae6

Salve esse código em um lugar seguro. Para restaurar de volta a arquivo binário, podemos fazer assim:

Terminal
xxd -r -p chave.hex pico-68ee8f51e038-restaurado.bin

O arquivo chave.hex deve conter o hash de 256 bits.



Associando o digest da chave ao firmware PicoKeys​


Agora vamos calcular o digest da chave pública para validar a assinatura do firmware:

Terminal
espsecure.py digest_sbv2_public_key \
--keyfile ~/.config/picokeys/signing/secure-boot.pem \
--output ~/.config/picokeys/signing/public-digest.bin

O comando acima gera um arquivo binário de 32 bytes, depois precisamos converter o conteúdo para o formato hexadecimal:

Terminal
python3 - ~/.config/picokeys/signing/public-digest.bin <<'PY'
from pathlib import Path
import sys

digest = Path(sys.argv[1]).read_bytes()
if len(digest) != 32:
raise SystemExit(f"Esperados 32 bytes; recebidos {len(digest)}")

for start in range(0, 32, 8):
print(" " + ", ".join(f"0x{b:02x}" for b in digest[start:start+8]) + ",")
PY

Tanto para FIDO quanto para HSM, edite o arquivo:

Terminal
vim pico-keys-sdk/src/otp/otp_esp32.c

Procure por esp_secure_boot_digest[32] e substitua os 32 bytes pelo resultado do comando anterior, assim como desmontrado no tutorial anterior. Essa alteração informa ao SDK qual digest será usado durante para validar a assinatura do firmware. Ela não assina o firmware e também não grava nada nos eFuses.



Configurando o Secure Boot e a Flash Encryption​


No diretório raiz de cada projeto, limpe o build e configure o alvo ESP32-S3:

Terminal
idf.py fullclean
idf.py set-target esp32s3
idf.py menuconfig

Em Security features, configure:

  • Enable hardware Secure Boot in bootloader;
  • Select secure boot version (Enable Secure Boot version 2);
  • Sign binaries during build;
  • Secure boot private signing key: caminho da chave RSA privada correspondente ao digest aceito pela placa;
  • Enable flash encryption on boot;
  • Enable usage mode: Release;
  • UART ROM download mode: (Permanently switch to Secure mode (recommended)).

Em Partition Table, altere o offset da tabela de 0x8000 para 0x10000. Salve com s e saia com q. Depois confira se as opções estão corretas:

Terminal
$ grep -E \
-e '^CONFIG_SECURE_BOOT=' \
-e '^CONFIG_SECURE_BOOT_V2_ENABLED=' \
-e '^CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES=' \
-e '^CONFIG_SECURE_BOOT_SIGNING_KEY=' \
-e '^CONFIG_SECURE_FLASH_ENC_ENABLED=' \
-e '^CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=' \
-e '^CONFIG_SECURE_FLASH_ENCRYPTION_AES128=' \
-e '^CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE=' \
-e '^CONFIG_PARTITION_TABLE_OFFSET=' \
sdkconfig
CONFIG_SECURE_BOOT=y
CONFIG_SECURE_BOOT_V2_ENABLED=y
CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES=y
CONFIG_SECURE_BOOT_SIGNING_KEY="/home/bruno/.config/picokeys/signing/secure-boot.pem"
CONFIG_SECURE_FLASH_ENC_ENABLED=y
CONFIG_SECURE_FLASH_ENCRYPTION_AES128=y
CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=y
CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE=y
CONFIG_PARTITION_TABLE_OFFSET=0x10000

Edite o arquivo abaixo, seja FIDO ou HSM:

Terminal
vim pico-keys-sdk/config/esp32/partitions.csv

A tabela padrão já usa factory em 0x20000 e part0 em 0x200000, com tamanho de 1M. Então não precisamos alterar esses valores, o mais importante é adicionar a flag encrypted no final da linha part0, ela deve ficar assim:

part0,    0x40,  0x1,     0x200000, 1M, encrypted

O ESP-IDF criptografa as partições de firmware. Já a partição part0 é de dados, por isso precisamos adicionar a flag encrypted, para que ela seja criptografada.


Confira as chaves antes de gravar

Este procedimento considera uma placa ESP32-S3 zerada, sem Secure Boot e Flash Encryption ativos. Antes de gravar o firmware, confirme que ele foi assinado com a chave privada que você gerou e que a chave de Flash Encryption usada para criptografar os arquivos é a mesma que será provisionada na placa.


Na primeira inicialização, o firmware pode gravar e proteger eFuses para ativar essas funções. Essa alteração é permanente. Faça um backup protegido das chaves e valide o build e as imagens no computador antes de iniciar a placa com esse firmware.



Gerando e validando o firmware assinado FIDO e HSM​


Agora vamos novamente ativar o ambiente ESP-IDF 5.5.1 (só por precaução) e compilar o Pico FIDO:

Terminal
. ~/src/picokeys-fe/esp-idf/export.sh && export IDF_TARGET=esp32s3

cd ~/src/picokeys-fe/pico-fido

idf.py -DIDF_TARGET=esp32s3 -DVIDPID=Yubikey5 -DENABLE_EDDSA=ON build

Estou usando -DENABLE_EDDSA=ON para ativar o suporte a chaves ed25519-sk para ser usado no SSH via FIDO.


Para o Pico HSM:

Terminal
. ~/src/picokeys-fe/esp-idf/export.sh && export IDF_TARGET=esp32s3

cd ~/src/picokeys-fe/pico-hsm

idf.py -DIDF_TARGET=esp32s3 -DVIDPID=YubiHSM build

Depois do build, precisamos validar as assinaturas do bootloader e da aplicação, igual o procedimento anterior. Para FIDO:

Terminal
cd ~/src/picokeys-fe/pico-fido

$ idf.py secure-verify-signature \
--version 2 \
--keyfile ~/.config/picokeys/signing/secure-boot.pem \
bootloader/bootloader.bin
Executing action: secure-verify-signature
Running espsecure in directory /home/bruno/src/picokeys-fe/pico-fido/build
Executing "/home/bruno/.espressif/python_env/idf5.5_py3.12_env/bin/python -m espsecure verify_signature --version 2 --keyfile /home/bruno/.config/picokeys/signing/secure-boot.pem bootloader/bootloader.bin"...
espsecure.py v4.12.0
Signature block 0 is valid (RSA).
Signature block 0 verification successful using the supplied key (RSA).
Signature block 1 invalid. Skipping.
Signature block 2 invalid. Skipping.


$ idf.py secure-verify-signature \
--version 2 \
--keyfile ~/.config/picokeys/signing/secure-boot.pem \
pico_fido.bin
Executing action: secure-verify-signature
Running espsecure in directory /home/bruno/src/picokeys-fe/pico-fido/build
Executing "/home/bruno/.espressif/python_env/idf5.5_py3.12_env/bin/python -m espsecure verify_signature --version 2 --keyfile /home/bruno/.config/picokeys/signing/secure-boot.pem pico_fido.bin"...
espsecure.py v4.12.0
Signature block 0 is valid (RSA).
Signature block 0 verification successful using the supplied key (RSA).
Signature block 1 invalid. Skipping.
Signature block 2 invalid. Skipping.

Para HSM:

Terminal
cd ~/src/picokeys-fe/pico-hsm

idf.py secure-verify-signature \
--version 2 \
--keyfile ~/.config/picokeys/signing/secure-boot.pem \
bootloader/bootloader.bin
Executing action: secure-verify-signature
Running espsecure in directory /home/bruno/src/picokeys-fe/pico-hsm/build
Executing "/home/bruno/.espressif/python_env/idf5.5_py3.12_env/bin/python -m espsecure verify_signature --version 2 --keyfile /home/bruno/.config/picokeys/signing/secure-boot.pem bootloader/bootloader.bin"...
espsecure.py v4.12.0
Signature block 0 is valid (RSA).
Signature block 0 verification successful using the supplied key (RSA).
Signature block 1 invalid. Skipping.
Signature block 2 invalid. Skipping.


idf.py secure-verify-signature \
--version 2 \
--keyfile ~/.config/picokeys/signing/secure-boot.pem \
pico_hsm.bin
Executing action: secure-verify-signature
Running espsecure in directory /home/bruno/src/picokeys-fe/pico-hsm/build
Executing "/home/bruno/.espressif/python_env/idf5.5_py3.12_env/bin/python -m espsecure verify_signature --version 2 --keyfile /home/bruno/.config/picokeys/signing/secure-boot.pem pico_hsm.bin"...
espsecure.py v4.12.0
Signature block 0 is valid (RSA).
Signature block 0 verification successful using the supplied key (RSA).
Signature block 1 invalid. Skipping.
Signature block 2 invalid. Skipping.

Para cada comando, o bloco de assinatura 0 precisa ser válido. Os blocos 1 e 2 podem aparecer como inválidos se não foram usados. Não grave um firmware cuja assinatura não tenha sido validada.



Preparação da placa e da chave FE - Somente Primeiro Firmware​


Conecte somente a placa que será usada (essa placa não pode ter secure boot ativo). Depois execute o processo para fazer a placa entrar no Download Mode (Segure BOOT, pressione e solte RESET e então solte BOOT).


Depois execute o comando abaixo para ver em qual porta ele subiu:

Terminal
$ ls -l /dev/ttyACM* /dev/ttyUSB*
/dev/ttyACM0

No meu caso foi em /dev/ttyACM0, vou usar esse nome no comando abaixo para gravar o firmware. Se o use foi outro, use o nome que apareceu ai, mas certifique-se de que é o ESP32-S3 correto que você quer gravar.


Agora temos que confirmar algumas coisas, comece executando os comandos abaixo:

Terminal
$ espefuse.py --chip esp32s3 --port "/dev/ttyACM0" summary | grep -E 'ENABLE_SECURITY_DOWNLOAD|DIS_DOWNLOAD_MODE'
DIS_DOWNLOAD_MODE (BLOCK0) Set this bit to disable download mode (boot_mode[3 = False R/W (0b0)
ENABLE_SECURITY_DOWNLOAD (BLOCK0) Set this bit to enable secure UART download mode = False R/W (0b0)

$ esptool.py --chip esp32s3 --port "/dev/ttyACM0" get_security_info | grep -iE 'Secure Boot|Flash Encryption|SPI_BOOT_CRYPT_CNT|BLOCK_KEY1'
BLOCK_KEY1 - USER/EMPTY
Secure Boot: Disabled
Flash Encryption: Disabled
SPI Boot Crypt Count (SPI_BOOT_CRYPT_CNT): 0x0

Esses comandos servem para confirmar alguns dados, como:

  • Secure Boot está desabilitado.
  • Flash Encryption está desabilitada e SPI_BOOT_CRYPT_CNT está em 0.
  • Secure Download Mode está desabilitado.
  • O bloco que será usado para FE está vazio e livre.

No procedimento abaixo, BLOCK_KEY1 fica reservado para Flash Ecryption e BLOCK_KEY0 para o digest do Secure Boot. Se os comandos acima não confirmarem que BLOCK_KEY1 está livre, pare e não substitua o bloco por outro sem rever a alocação das chaves.


Agora como requisito da documentação oficial temos que limpar a flash (não vejo porque isso seria necessário, mas vamos seguir a risca) e confira se a chave AES-XTS tem 32 bytes:

Terminal
$ esptool.py --chip esp32s3 --port "/dev/ttyACM0" erase_flash
esptool.py v4.12.0
Serial port /dev/ttyACM0
Connecting...
Chip is ESP32-S3 (QFN56) (revision v0.2)
Features: WiFi, BLE, Embedded PSRAM 8MB (AP_3v3)
Crystal is 40MHz
USB mode: USB-Serial/JTAG
MAC: 68:ee:8f:51:e0:38
Uploading stub...
Running stub...
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 4.7 seconds.
Hard resetting via RTS pin...

Agora vamos enviar a chave usada no Flash Encryption para a posição BLOCK_KEY1 da eFuse:

Terminal
$ espefuse.py --chip esp32s3 \
--port "/dev/ttyACM0" \
burn_key BLOCK_KEY1 "/home/bruno/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin" XTS_AES_128_KEY
espefuse.py v4.12.0
Connecting...

=== Run "burn_key" command ===
Sensitive data will be hidden (see --show-sensitive-info)
Burn keys to blocks:
- BLOCK_KEY1 -> [?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??]
Reversing byte order for AES-XTS hardware peripheral
'KEY_PURPOSE_1': 'USER' -> 'XTS_AES_128_KEY'.
Disabling write to 'KEY_PURPOSE_1'.
Disabling read to key block
Disabling write to key block


Check all blocks for burn...
idx, BLOCK_NAME, Conclusion
[00] BLOCK0 is not empty
(written ): 0x0000000000000100000000000000d1f50000000000000000
(to write): 0x000000000000000000000000400000000000000201000200
(coding scheme = NONE)
[05] BLOCK_KEY1 is empty, will burn the new value
.
This is an irreversible operation!
Type 'BURN' (all capitals) to continue.
BURN
BURN BLOCK5 - OK (write block == read block)
BURN BLOCK0 - OK (all write block bits are set)
Reading updated efuses...
Successful

O burn_key protege a chave contra leitura e escrita por padrão.


O procedimento vai pedir para você digitar BURN para continuar o processo, digite e aperte Enter.


Agora vamos conferir o get_security_info novamente, o BLOCK_KEY1 deve ter finalidade XTS_AES_128_KEY e estar protegido contra leitura e escrita.

Terminal
$ esptool.py --chip esp32s3 --port "/dev/ttyACM0" get_security_info | grep -iE 'Secure Boot|Flash Encryption|SPI_BOOT_CRYPT_CNT|BLOCK_KEY1'
BLOCK_KEY1 - XTS_AES_128_KEY
Secure Boot: Disabled
Flash Encryption: Disabled
SPI Boot Crypt Count (SPI_BOOT_CRYPT_CNT): 0x0


Gravando o primeiro firmware na placa​


Vamos juntar os arquivos em um único binário:

Terminal
### FIDO:

cd ~/src/picokeys-fe/pico-fido && mkdir -p release && cd build

esptool.py --chip ESP32-S3 merge_bin \
-o ../release/pico_fido_esp32-s3.bin \
--flash_mode dio --flash_freq 80m --flash_size keep \
0x0 bootloader/bootloader.bin \
0x10000 partition_table/partition-table.bin \
0x20000 pico_fido.bin && cd ..

### HSM:

cd ~/src/picokeys-fe/pico-hsm && mkdir -p release && cd build

esptool.py --chip ESP32-S3 merge_bin \
-o ../release/pico_hsm_esp32-s3.bin \
--flash_mode dio --flash_freq 80m --flash_size keep \
0x0 bootloader/bootloader.bin \
0x10000 partition_table/partition-table.bin \
0x20000 pico_hsm.bin && cd ..

No layout acima, os endereços usados são:

ArquivoEndereço
Bootloader0x0
Tabela de partições0x10000
Aplicação/Firmware (factory)0x20000
Dados (part0), se houver conteúdo0x200000

Agora vamos gravar na placa:

Terminal
# FIDO:
esptool.py --chip ESP32-S3 -p "/dev/ttyACM0" write_flash \
--flash_mode dio --flash_freq 80m --flash_size keep \
0x0 release/pico_fido_esp32-s3.bin


# HSM:
esptool.py --chip ESP32-S3 -p "/dev/ttyACM0" write_flash \
--flash_mode dio --flash_freq 80m --flash_size keep \
0x0 release/pico_hsm_esp32-s3.bin

write_flash vs encrypt_flash_data

O write_flash é usado com os binários assinados, mas quando a placa ainda não possui Flash Eencryption ativa. Como nós configuramos Flash Encryption on boot e já colocamos a chave no eFuse, é o bootloader quem vai fazer a criptografia na primeira inicialização do firmware na placa.


Nas próximas gravações, depois que a Flash Encryption estiver ativa, vamos usar o encrypt_flash_data para criptografar o novo firmware no computador com a chave Flash Encryption da placa. Depois, vamos gravar o arquivo criptografado com o write_flash, usando o mesmo endereço definido na criptografia.


Desconecte e reconecte a placa e entre em modo de download, depois execute o comando abaixo:

Terminal
$ esptool.py --chip ESP32-S3 --port /dev/ttyACM0 --no-stub get_security_info
esptool.py v4.12.0
Serial port /dev/ttyACM0
Connecting...
Chip is ESP32-S3 in Secure Download Mode
Enabling default SPI flash mode...

Security Information:
=====================
Flags: 0x000006f5 (0b11011110101)
Key Purposes: (9, 4, 0, 0, 0, 0, 12)
BLOCK_KEY0 - SECURE_BOOT_DIGEST0
BLOCK_KEY1 - XTS_AES_128_KEY
BLOCK_KEY2 - USER/EMPTY
BLOCK_KEY3 - USER/EMPTY
BLOCK_KEY4 - USER/EMPTY
BLOCK_KEY5 - USER/EMPTY
Chip ID: 9
API Version: 0
Secure Boot: Enabled
Secure Boot Key Revocation Status:

Secure Boot Key1 is Revoked

Secure Boot Key2 is Revoked

Flash Encryption: Enabled
SPI Boot Crypt Count (SPI_BOOT_CRYPT_CNT): 0x7
Dcache in UART download mode: Disabled
Icache in UART download mode: Disabled
JTAG: Permanently Disabled
Hard resetting via RTS pin...

A linha BLOCK_KEY0 - SECURE_BOOT_DIGEST0 mostra que o digest da chave pública do Secure Boot foi gravado no BLOCK_KEY0. As mensagens Secure Boot Key1 is Revoked e Secure Boot Key2 is Revoked mostram que os slots 1 e 2 foram revogados. Assim, o slot 0 continua autorizado para validar as assinaturas.


Também podemos observar que o Flash Encryption está ativo. O valor SPI_BOOT_CRYPT_CNT: 0x7 confirma que a Flash Encryption foi ativada, e BLOCK_KEY1 - XTS_AES_128_KEY mostra que a chave FE está no bloco esperado. A placa também está em Secure Download Mode. Nesse modo, o download fica restrito aos comandos básicos de gravação. As caches de dados e instruções estão desativadas durante o download, e o JTAG foi permanentemente desabilitado.



Gravando novos firmwares criptografados - segunda gravação ou posteriores​


Este procedimento é para a segunda gravação ou posteriores, depois que o primeiro firmware já ativou a Flash Encryption. Depois de compilar o firmware, vamos juntar os binários em um úncio arquivo:

Terminal
### FIDO:

cd ~/src/picokeys-fe/pico-fido && mkdir -p release && cd build

esptool.py --chip ESP32-S3 merge_bin \
-o ../release/pico_fido_esp32-s3.bin \
--flash_mode dio --flash_freq 80m --flash_size keep \
0x0 bootloader/bootloader.bin \
0x10000 partition_table/partition-table.bin \
0x20000 pico_fido.bin && cd ..


### HSM:

cd ~/src/picokeys-fe/pico-hsm && mkdir -p release && cd build

esptool.py --chip ESP32-S3 merge_bin \
-o ../release/pico_hsm_esp32-s3.bin \
--flash_mode dio --flash_freq 80m --flash_size keep \
0x0 bootloader/bootloader.bin \
0x10000 partition_table/partition-table.bin \
0x20000 pico_hsm.bin && cd ..

Agora vamos criptografar o firmware:

Terminal
# FIDO:
espsecure.py encrypt_flash_data --aes_xts \
--keyfile "/home/bruno/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin" \
--address 0x0 \
--output release/pico_fido_esp32-s3.enc.bin \
release/pico_fido_esp32-s3.bin

# HSM:
espsecure.py encrypt_flash_data --aes_xts \
--keyfile "/home/bruno/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin" \
--address 0x0 \
--output release/pico_hsm_esp32-s3.enc.bin \
release/pico_hsm_esp32-s3.bin

Antes de gravar, vamos fazer um teste para validar que a criptografia deu certo:

Terminal
espsecure.py decrypt_flash_data --aes_xts \
--keyfile "/home/bruno/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin" \
--address 0x0 \
--output /tmp/pico_hsm.dec.bin \
release/pico_hsm_esp32-s3.enc.bin

# Compare a diferença entre o firmware original (criptografado) e o firmware recem descriptografado (se nao retornar nada, está tudo certo):
cmp release/pico_hsm_esp32-s3.bin /tmp/pico_hsm.dec.bin

Para gravar precisamos usar o parâmetro --force, se não usar, isso acontece:

Terminal
$ esptool.py --chip ESP32-S3 -p "/dev/ttyACM0" --no-stub write_flash \
--flash_mode dio --flash_freq 80m --flash_size keep \
0x0 release/pico_hsm_esp32-s3.enc.bin
esptool.py v4.12.0
Serial port /dev/ttyACM0
Connecting...
Chip is ESP32-S3 in Secure Download Mode
Enabling default SPI flash mode...
Configuring flash size...
WARNING: In case of failure, please set a specific --flash_size.

A fatal error occurred: WARNING: Detected flash encryption and secure download mode enabled.
Flashing plaintext binary may brick your device! Use --force to override the warning.

Esse aviso é esperado do esptool, já que ele encontrou uma placa com Flash Encryption e Secure Download Mode habilitados. Quando ele encontra esses recursos ativos, não tem como ele verificar se o arquivo foi criptografado e se foi da forma correta, por isso precisamos usar o --force.


Como nós validamos a criptografia, podemos seguir:

Terminal
# FIDO:
esptool.py --chip ESP32-S3 -p "/dev/ttyACM0" --no-stub write_flash --force \
--flash_mode dio --flash_freq 80m --flash_size keep \
0x0 release/pico_fido_esp32-s3.enc.bin


# HSM:
esptool.py --chip ESP32-S3 -p "/dev/ttyACM0" --no-stub write_flash --force \
--flash_mode dio --flash_freq 80m --flash_size keep \
0x0 release/pico_hsm_esp32-s3.enc.bin


Fontes​


https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/security/flash-encryption.html

https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/security/secure-boot-v2.html

https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/security/security-features-enablement-workflows.html

https://github.com/polhenarejos/pico-fido

https://github.com/polhenarejos/pico-hsm