ESP32 - Segurança e proteção da flash

Neste artigo trataremos, de forma fácil e rápida, um assunto muito importante para quem pretende comercializar produtos com o ESP32, a segurança do seu hardware com o código presente na memória flash, a fim de impedir clonagem, furto e etc.

 

Utilizaremos a ESP-IDF v4.0-dev-76-g96aa08a0f-dirty (Ubuntu) para todos artigos desta série e não será abordado sobre como utilizar a IDF, sendo dever do leitor conhecer o funcionamento. Mais sobre a IDF: https://github.com/espressif/esp-idf

 

Figura 1 - Protegendo o ESP32.

 

Explicando em miúdos

 

Todo código (firmware) transferido ao ESP32 fica salvo, na maioria das versões, na memória flash externa, que diminui ainda mais a segurança, já que alguém pode simplesmente removê-la para leitura em um hardware externo e clonar, em segundos, nosso código que pode ter demorado anos para ser desenvolvido. Mesmo se a flash for embutida, como na versão “PICO”, é possível exportar todo conteúdo da flash com apenas um comando no terminal. Então, se todo conteúdo pode ser facilmente obtido, devemos nos proteger e é isso que a criptografia da flash do ESP32 nos proporciona.

 

Criptografia da flash

Atenção

  • Não abordaremos todas funções e características da criptografia da flash, sendo necessário que você estude MUITO BEM a documentação oficial, a fim de evitar qualquer dor de cabeça que pode ocorrer conforme a IDF se atualiza. Não somos responsáveis por qualquer uso errado de sua parte.
  • A criptografia da flash limita como e/ou quantas vezes é possível fazer upload de novos códigos. Caso feito incorretamente, você pode perder seu ESP32 e não será mais possível regrava-lo.
  • Abordaremos, por motivos de didática, apenas sobre a criptografia com uma chave pré-gerada, assim, podemos regravar o ESP32 sem qualquer restrição de quantidade.
  • Em ambientes que é necessário a maior segurança disponível, você não deve utilizar uma chave pré-gerada, deixando o próprio ESP32 gerar a sua, sendo individual para cada hardware. Além de também habilitar o Secure boot que não abordaremos aqui.

 

A criptografia da flash (AES-256) é uma característica presente no ESP32 que criptografa o conteúdo presente na flash. Quando habilitado, leituras físicas sem a chave não são suficientes para recuperar o conteúdo. Sendo assim, nos protegemos de quem tentar exportá-la para clonagem e etc. A chave é gravada em um bloco de eFuse, que pode ser protegido contra leitura e escrita (padrão) e, conhecendo a chave, podemos regravar códigos sem a limitação de quantidade, diferentemente do caso onde o ESP32 gera sua própria chave, onde estamos limitados em até 3 uploads físicos.

 

Vamos observar alguns itens relevantes sobre a criptografia:

  • Em um upload plaintext, o binário original (cru) é enviado ao microcontrolador.
  • Em um upload criptografado, o binário é enviado ao microcontrolador já criptografado pela IDF.
  • O eFuse “FLASH_CRYPT_CNT” (7-bit) é responsável pela permissão de uploads plaintext, pela contagem de uploads físicos plaintext (até 3x) e pelo controle do bootloader para criptografar o conteúdo da flash. Pode ser protegido contra R/W. Após 3 uploads plaintext, este eFuse chegará em seu máximo e aceitará apenas uploads criptografados.
    • Quando for um número par, o bootloader irá criptografar todo conteúdo da flash, logo, é necessário o upload plaintext.
    • Quando for um número ímpar, o bootloader não irá criptografar o conteúdo da flash, logo, é necessário o upload criptografado.
  • Se a chave não for conhecida (pré-gerada), temos no máximo 3 uploads físicos disponíveis (plaintext), que também nos permite desabilitar a criptografia. Se o “FLASH_CRYPT_CNT” for protegido enquanto ímpar, não será possível novos uploads plaintext.
  • Os binários “Bootloader”, “Partition table”, “OTA DATA”, todas “APP (seu código)” e as partições marcadas com a flag “encrypted” na tabela de partição serão criptografados. Partições que não estiverem marcados com “encrypted” não serão criptografados e poderão ser lidos externamente, tenha atenção ao utilizar APIs para acesso da flash como NVS e SPIFFS.
  • Se o “FLASH_CRYPT_CNT” não for protegido corretamente e/ou ainda houver alguma tentativa para upload plaintext, invasores podem inserir códigos maliciosos e ler o conteúdo de forma descriptografada pelo próprio ESP32 sem conhecimento da chave. Por causa disto, é comum protegê-lo contra escrita, a fim de evitar uploads plaintext.

 

Cientes dos detalhes básicos (há dezenas de detalhes extras na documentação oficial que você deve ler antes de efetuar os testes abaixo), vamos testar a criptografia da flash e ver se realmente funciona! Lembrando que utilizaremos a IDF no Ubuntu e quase todos comandos podem mudar de acordo com seu computador, projeto, endereços e muitas outras coisas. Os comandos utilizados aqui podem não servir para você, logo, terá que alterá-los de acordo com seu projeto, caminho de arquivos e etc. Este artigo é apenas uma demonstração e deve ser tomado como base, não seguido ao pé da letra. Os scripts utilizados estão na pasta da IDF, “esp-idf/components/esptool_py/esptool/”, tome bastante atenção ao uso dos caminhos dos arquivos e scripts, pois você pode estar tentando rodar o comando no local errado. Também tenha atenção na porta utilizada, seu ESP32 pode estar em uma porta diferente da nossa.

 

Primeiramente, vamos testar um código simples apenas para verificar sobre o que foi citado acima, sobre a leitura (clonagem) da memória flash sem proteção. Você pode ignorar essa parte.

 

Quando fazemos um upload padrão para placa, o computador utiliza um script python (esptool.py) com o comando “make flash”, que basicamente compila e faz upload (plaintext). Com o código enviado, vamos fazer uma leitura (sumário) dos eFuses para comparação após ativar a criptografia:

 

Sabendo que este ESP32 não está protegido, podemos exportar o conteúdo da flash e usar para clonagem, engenharia reversa ou o que der na cabeça! Vamos ver se achamos a palavra usada no ESP_LOGI(), “Embarcados...”, ao ler o conteúdo da memória:

 

Utilizando o comando “hexdump” para ler o arquivo gerado, conseguimos achar a palavra utilizada no código sem criptografia:

Figura 2 - Memória do ESP32 clonada antes da criptografia.

 

Agora que a clonagem foi demonstrada com apenas um comando no terminal, indicando que seu produto sem proteção está totalmente vulnerável nas mãos de alguém, vamos nos proteger ativando a criptografia.

 

  1. Criando a chave

 

Utilizaremos o próprio script da IDF para criar uma chave (256b), mas você pode utilizar qualquer método ou chave que desejar. O comando exportará a chave no arquivo “key.bin”, que você deve deixar uma cópia dentro da pasta do seu projeto, ficando algo similar com o nosso:

 

Figura 3 - Pasta do projeto com chave.
  1. Gravando a chave pré-gerada no eFuse

 

O comando acima gravou nossa chave no eFuse e automaticamente protegeu contra R/W, o que impede de qualquer um conseguir lê-la ou alterá-la. Apesar deste comando escrever no terminal a chave gravada, ao tentar ler a chave diretamente do eFuse (sumário) como feito anteriormente, é retornado:

 

Agora com uma chave conhecida no eFuse, temos a possibilidade de uploads ilimitados (criptografados), o que é muito interessante na fase de desenvolvimento.

  1. Ativando a criptografia da flash

Agora com a chave gravada, basta ativar a criptografia da flash no “menuconfig” em “Security features”.

Figura 4 - Ativando a criptografia da flash.

Atenção, se você não gravou a chave pré-gerada, ao dar o upload de um novo código com a criptografia ativada, o próprio ESP32 irá gerar uma chave que nem você, nós ou a Espressif poderá ler, lhe restando 3 uploads (plaintext) e/ou tentativas para desativar a criptografia, tome cuidado! Atualizações OTA são ilimitadas mesmo sem conhecimento da chave.

 

Após ativar a criptografia, vamos refazer o upload do código utilizado anteriormente de forma padrão, como utilizado antes (upload plaintext). Nesse primeiro boot, o bootloader irá criptografar todo conteúdo da memória e reiniciará, esse processo pode demorar um pouco então aguarde. Após o ESP32 reiniciar, indicando que a criptografia foi ativada, vamos fazer uma nova leitura da flash para ver se encontramos a palavra “Embarcados...” novamente.

 

Figura 5 - Memória do ESP32 clonada após criptografia.

 

Além da palavra “Embarcados...” não ser encontrada, nenhum texto legível foi visto, o que anteriormente era facilmente visto (Strings utilizadas pela própria IDF). O mesmo endereço que encontramos a palavra anteriormente, agora, não passa de um texto corrompido para quem tentar ler sem a chave!

 

Se olharmos o sumário dos eFuses novamente, podemos ver que o “FLASH_CRYPT_CNT” foi de 0 para 1, indicando que agora só aceita uploads criptografados, logo, se utilizarmos o upload padrão (plaintext), o ESP irá ficar em boot-loop com a seguinte mensagem:

 

O “FLASH_CRYPT_CNT” não é protegido contra escrita por padrão, logo, alguém ainda pode desativar a criptografia, fazer upload (plaintext) e ler sua flash de forma descriptografada, logo, devemos proteger este eFuse contra escrita ENQUANTO estiver em um número ímpar ou utilizar o Secure boot.

  1. Protegendo o eFuse

Vamos proteger o eFuse “FLASH_CRYPT_CNT” contra escritas para impossibilitar qualquer tipo de upload sem conhecimento da chave, isso automaticamente permite que suas placas estejam protegidas de uploads não permitidos, forçando seu hardware a aceitar apenas códigos que tenham sidos criptografados com a chave. Apesar desta proteção funcionar com o mesmo intuito do Secure boot (impossibilitar uploads não permitidos), há alguns casos em que manter o Secure boot ativado pode ser melhor, entretanto, na maioria dos casos, protegendo o eFuse já não precisamos do Secure boot (pesquise melhor sobre isso se for usar em ambientes agressivos onde a segurança deve prevalecer).

 

Antes de proteger a escrita, devemos certificar-se que ele está em algum número ímpar através do sumário, que nos retornou “FLASH_CRYPT_CNT Flash encryption mode counter = 1 R/W (0x1)”

 

A partir de agora, é impossível desabilitar a criptografia ou enviar códigos que não estejam criptografados pela chave presente no ESP32.

 

Ok, se o upload padrão não funciona mais, o que faremos? Criptografamos antes de enviar! Apesar de ser utilizado AES-256, a Espressif usa métodos diferentes de funcionamento (detalhes na documentação oficial), logo, precisamos criptografar pelo próprio script da IDF

  1. Criptografando os binários

O upload agora deve ser feito “manualmente”, sendo necessário criptografar e enviar os binários criptografados. Essa parte depende muito de projeto para projeto, principalmente nos caminhos de arquivos e endereço da memória, tome muita atenção.

5.1 Crie uma pasta “enc” dentro do seu projeto, ela irá guardar os binários criptografados pelo script.

 

5.2 Com o terminal aberto na pasta do seu projeto, use o comando “make all” para descobrir o que e onde os arquivos são gravados.

 

Podemos ver que nesse nosso projeto, onde utilizamos OTA e uma tabela de partições (custom), é feito o upload de 4 binários nos seus respectivos endereços.

ota_data_initial.bin: 0x363000.

bootloader.bin: 0x1000.

esp32.bin (o código em si): 0x10000.

partitions.bin: 0x8000.

 

Iremos criptografá-los individualmente e posteriormente, efetuar o upload. Não se esqueça que os caminhos dos arquivos devem ser alterados para o seu projeto.

5.3 Com o terminal aberto na pasta de scripts, use os comandos abaixo e não se esqueça de tomar muita atenção com os endereços de memória e caminho dos binários.

5.4 Faça o upload dos binários criptografados pelo comando: 

 

Após o upload criptografado, o ESP32 iniciará normalmente como se nada tivesse acontecido. Esse método apesar de ser manual, pode ser automatizado por um script (bash), basta colocá-los num arquivo e executar no terminal.

 

Agora que a proteção do ESP32 esta ativada, podemos ficar mais relaxados na questão sobre alguém clonar nosso firmware, já que não será possível sem a chave gravada. Você deve ler toda documentação oficial e também pode ser interessante utilizar o Secure boot sem criptografia da flash, análise seu projeto e mãos na massa!

 

Referências

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

NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

José Morais
Estudante de Engenharia da Computação pela USC, pretende se aprimorar e fazer a diferença nesta imensa área da tecnologia. Apaixonado por IoT, sistemas embarcados, microcontroladores e integração da computação nos mais diversos fins práticos e didáticos.

1
Deixe um comentário

avatar
 
1 Comment threads
0 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
1 Comment authors
Alexandre Fernandes dos Anjos Recent comment authors
  Notificações  
recentes antigos mais votados
Notificar
Alexandre Fernandes dos Anjos
Membro

Ótimo artigo José, muito didático e detalhado...