Secure Boot integrado com o Yocto pt.1 (Bootloader)

{getToc} $title={Índice}

Informações


Nível de dificuldade: Médio/Avançado

Requisitos: Conhecimento em Linux Embarcado, Yocto, Devtool e noção básica de Secure Boot.


Ressalvas

  1. Este tutorial utiliza o iMX8QXP, caso o seu processador seja diferente, algumas adaptações deverão ser realizadas, como por exemplo, endereço dos Efuses, AHAB para HABv3 ou HABv4 no caso de versões mais antigas do iMX.
  2. Dependendo da imagem/versão do Yocto utilizada para o build, o sistema de diretório/bootloader pode ter algumas diferenças.
  3. Comandos iniciando com "$" são utilizados na maquina host.
  4. Comandos iniciando com "=>" são utilizados no bootloader do target.
  5. Apesar das possíveis discrepâncias, esse tutorial irá fornecer uma visal geral dos processos necessários para o uso do Secure Boot em um sistema Linux Embarcado.

Introdução

Este tutorial demonstra a implementação de um sistema Secure Boot (AHAB) para o processador iMX8QXP, em um ambiente Yocto.

Habilitação do Secure Boot (AHAB)

Para habililtar a funcionalidade do AHAB primeiro é necessário adicioná-lo no defconfig da placa
Ativar o ahab no defconfig da placa:

Local padrão: <yocto-workspace>/build-wayland/workspace/sources/u-boot-imx/configs/imx8qxp_mek_defconfig

Adicionar a linha:

CONFIG_AHAB_BOOT=y

Geração de árvore PKI, tabela SRK e Hash dos Efuses

Os próximos passos descrevem a geração da árvore PKI, tabela SRK e Hash dos Efuses.

Após esses procedimentos você terá todas as ferramentas disponíveis para assinar uma imagem e programar os Efuses (OTP) da sua placa.

Geração de árvore PKI

  • Criar senha para uso do CST:
No diretório <CST_workspace>/release/keys/
Criar um arquivo txt chamado key_pass.txt, e inserir uma senha para utilização do CST (por exemplo pki_pass_123)
Você deve colocar a senha duas vezes nesse arquivo, por exemplo:

senha_escolhida
senha_escolhida


  • Criar número serial para geração da PKI Tree:
No diretório <CST_workspace>/release/keys/
Criar um arquivo chamado "serial" (não colocar .txt como extensão), com um conteúdo de 8 dígitos à sua escolha, para geração da Tree, por exemplo 12345678.


  • Executar o Script de geração da PKI Tree:
Esse script está localizado em <CST_workspace>/release/keys/ahab_pki_tree.sh
Executar o script e configurar conforme desejado.
A configuração utilizada nesse tutorial foi a seguinte:


Dúvidas de configuração consultar o HAB Code Signing Tool User's Guide, contido no repositório da ferramenta CST.

Geração de tabela SRK e Hash dos fuses


Ir ao diretório <CST_workspace>/release/crts e utilizar o comando:

$../linux64/bin/srktool -a -s sha384 -t SRK1234table.bin -e SRK1234fuse.bin -f 1 -c SRK1_sha384_secp384r1_v3_usr_crt.pem,SRK2_sha384_secp384r1_v3_usr_crt.pem,SRK3_sha384_secp384r1_v3_usr_crt.pem,SRK4_sha384_secp384r1_v3_usr_crt.pem

Os arquivos gerados estarão na pasta <CST_workspace>/release/crts, com os nomes inseridos como argumento acima (nesse caso SRK1234table.bin e SRK1234fuse.bin).

Assinatura dos containers e montagem do bootloader

No caso do sistema utilizado nesse tutorial, o binário de boot é composto por dois containers: boot-spl-container.img u-boot-atf-container.img
Ambos containers precisam ser assinados, para depois ser montado o binário de boot final.
Um dos métodos para verificar se o seu bootloader possui dois containers, é bootar a placa depois de ativar o AHAB no defconfig e usar o comando dentro do boot:

=>ahab_status

Algumas mensagens de debug do SECO (Security Controller) deverão aparecer:
  • Caso os Efuses da placa ainda não tenham sido programados com o Hash gerado:
SECO Event[0] = 0x0087FA00
CMD = AHAB_AUTH_CONTAINER_REQ (0x87)
IND = AHAB_BAD_KEY_HASH_IND (0xFA)
  • Caso tenha algum container sem assinatura:
SECO Event[1] = 0x0087EE00
CMD = AHAB_AUTH_CONTAINER_REQ (0x87)
IND = AHAB_NO_AUTHENTICATION_IND (0xEE)


O SECO Event 0x0087EE00 irá aparecer duas vezes caso o seu bootloader possua dois containers sem nenhuma assinatura.

Obtendo os arquivos para assinatura da imagem

Primeiro é necessário pegar os containers antes da montagem do binário final, dentro do ambiente Yocto.
Os dois arquivos necessários são:

boot-spl-container.img
u-boot-atf-container.img

Após realizar o build da sua imagem, os arquivos necessários estarão dentro do seguinte diretório:

<yocto-workspace>/build-wayland/tmp/work/imx8qxpc0mek-poky-linux/imx-boot/1.0-r0/git/iMX8QX

Copiar esses dois arquivos pro seu workspace do CST, em uma pasta nova (neste exemplo criei uma pasta unsigned_containers)

Scripts de assinatura e configuração de offsets

  • Obtenção dos scripts:
Para a assinatura das imagens é possível utilizar dois scripts de exemplo presentes no diretório do u-boot-imx:

<yocto-workspace>/build-wayland/workspace/sources/u-boot-imx/doc/imx/ahab/csf_examples (no caso de workspace montado pelo devtool para o u-boot-imx)

Os arquivos são:

csf_boot_atf.txt
csf_boot_image.txt

  • Obtenção dos offsets de cada container:
É preciso configurar os scripts de assinatura com os offsets corretos de cada container.
Esses offsets podem ser encontrados no ambiente Yocto no arquivo:

<workspace>/build-wayland/tmp/work/imx8qxpc0mek-poky-linux/imx-boot/1.0-r0/temp/log.do_compile

Esse log contém diversas informações, porém estamos interessados apenas no offset da construção do u-boot-atf-container.img e do boot-spl-container.img. Pegar o valor de offset e do container offset de cada um.

  • Configurar os scripts de assinatura:
Copiar os containers da pasta "unsigned_containers" para o <CST workspace>.
Atualizar o arquivo csf_boot_atf.txt com os offsets do u-boot-atf-container.img (Container offset e Signature Block offset)
Conferir o caminho do local do script até a sua tabela SRK e o source da chave (campo File e campo Source)
Atualizar o arquivo csf_boot_image.txt com os offsets do boot-spl-container.img (Container offset e Signature Block offset)
Conferir o caminho do local do script até a sua tabela SRK e o source da chave (campo File e campo Source)

  • Assinar os containers:
Utilizar os comandos:

$./release/linux64/bin/cst -i csf_boot_image.txt -o flash.signed.bin
$./release/linux64/bin/cst -i csf_uboot_atf.txt -o u-boot-atf-container.signed.img

Que deverá retornar o seguinte para cada comando:
"CSF Processed successfully and signed image available in <img name>"


  • Montagem do bootloader final:
Para facilitar o merge dos containers, foi retirado e adaptado um pedaço de um makefile do Yocto.
O arquivo deve ser criado com o nome Makefile e colocado no <CST workspace> com o seguinte conteúdo:

merge_images:
@flashbin_size=`wc -c flash.signed.bin | awk '{print $$1}'`; \
pad_cnt=$$(((flashbin_size + 0x400 - 1) / 0x400)); \
echo ""append u-boot-atf-container.img at $$pad_cnt KB""; \
dd if=u-boot-atf-container.signed.img of=flash.signed.bin bs=1K seek=$$pad_cnt;

Na pasta <CST workspace> executar:

$make merge_images

O output será um arquivo chamado flash.signed.bin
Esse arquivo é o bootloader final, com os dois containers assinados e que será utilizado para gravação na placa.

Verificação da assinatura das imagens (Opcional)

Para verificar se as imagens foram assinadas corretamente, é possível gravar a placa com o bootloader gerado.
Após o boot, utilizar o comando =>ahab_status, que não deve retornar nenhum erro do tipo 0x0087EE00, apenas um erro do tipo 0x0087FA00 (Efuses da OTP ainda não programados).

Gravação dos Efuses com o Hash SRK gerado

ATENÇÃO

O procedimento de gravação na OTP é irreversível, realizar os próximos passos com cuidado.
Caso a OTP seja programada com valores errados, o secure boot irá falhar na verificação de assinaturas, travando a placa no final do procedimento.


  • Verificar os valores de Hash do SRK:
No diretório <CST workspace>/release/crts executar o comando:

$od -t x4 SRK1234fuse.bin

  0000000 d436cc46 8ecccda9 b89e1601 5fada3db
  0000020 d454114a b6cd51f4 77384870 c50ee4b2
  0000040 a27e5132 eba887cf 592c1e2b bb501799
  0000060 ee702e07 cf8ce73e fb55e2d5 eba6bbd2

Esses são os valores Hash que devem ser programados nos efuses da sua placa.
Para gravar os Efuses executar a sequência abaixo:

=> fuse prog 0 730 0xd436cc46
=> fuse prog 0 731 0x8ecccda9
=> fuse prog 0 732 0xb89e1601
=> fuse prog 0 733 0x5fada3db
=> fuse prog 0 734 0xd454114a
=> fuse prog 0 735 0xb6cd51f4
=> fuse prog 0 736 0x77384870
=> fuse prog 0 737 0xc50ee4b2
=> fuse prog 0 738 0xa27e5132
=> fuse prog 0 739 0xeba887cf
=> fuse prog 0 740 0x592c1e2b
=> fuse prog 0 741 0xbb501799
=> fuse prog 0 742 0xee702e07
=> fuse prog 0 743 0xcf8ce73e
=> fuse prog 0 744 0xfb55e2d5
=> fuse prog 0 745 0xeba6bbd2


ATENÇÃO 1: Os valores de cada registro a ser programado (730, 731 e etc) variam de processador para processador (i.MX8QXP é diferente do i.MX8QM por exemplo), consultar quais os setores corretos para o seu.

ATENÇÃO 2: Os valores hexa a serem gravados também devem ser de acordo com seu output da tabela SRK1234fuse.bin, não usar os valores deste exemplo na íntegra.


  • Finalizando o processo:
Para testar se todo o processo deu certo, é só gravar a placa com o bootloader assinado e inicializar.
O comando =>ahab_status não deve retornar nenhum evento SECO, como a seguir:

> ahab_status
Lifecycle: 0x0020, NXP closed

No SECO Events Found!

  • Finalização do AHAB:
Até agora, mesmo com imagens sem assinatura ou assinatura errada, o sistema realiza o boot para que possamos verificar o debug do SECO.
Após fechar o ahab, qualquer imagem que falhe não irá nem entrar no boot, travando a placa.
Caso algum processo anterior tenha sido feito errado, a placa se tornará inutilizável.
Para finalizar é só executar o comando:

=>ahab_close

Rebootar a placa e utilizar o comando:

=>ahab_status

Agora o valor "lifecycle" agora deverá aparecer OEM closed.

Guilherme Rossi

Especialista em Sistemas Embarcados formado em Engenharia Elétrica. Experiência de atuação em linguagem C, Linux Embarcado, Microcontroladores, protocolos de comunicação e debug. linkedin

Postar um comentário

Deixe seu comentário ou sua sugestão!

Postagem Anterior Próxima Postagem

Formulário de contato