{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
- 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.
- Dependendo da imagem/versão do Yocto utilizada para o build, o sistema de diretório/bootloader pode ter algumas diferenças.
- Comandos iniciando com "$" são utilizados na maquina host.
- Comandos iniciando com "=>" são utilizados no bootloader do target.
- 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 placaAtivar 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.
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:
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:
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 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 e configurar conforme desejado.
senha_escolhida
- Criar número serial para geração da PKI Tree:
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:
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.
Ir ao diretório <CST_workspace>/release/crts e utilizar o comando:
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:
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:
<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:
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:
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:
$./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:
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:
$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:
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:
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.