Secure boot in i.MX6

Secure boot in i.MX6


When developing any project for embedded systems, the developer must solve two additional issues:

  • How to protect firmware from spoofing in the product;
  • How to protect software from copying.

This article describes how to protect the i.MX6 processor from changing the bootloader in the product and complicate the process of copying firmware.



Introduction


To protect intellectual property in case the bare metal project [1] mainly helps the hardware complex, both present in the microcontroller, and external. Examples are given on a reputable site [2] . Additional software methods, such as [3] are used, however, they are based on the unknown protection mechanism. This is due to the low computing power of the devices and the inconvenience of implementing dynamic firmware generation.

When SoC comes into play with operating systems like Linux, protection needs to be organized simultaneously at several levels:

  • End application level
  • Operating system level
  • Bootloader level

In case of gaining access at any of the levels, it is easy enough to influence the software of further levels and make changes to inter-level interactions.
The simplest examples: Your software installed on Linux communicates via the UART protocol (for example, through / dev / ttyACM0) with a microcontroller. An attacker gaining access to the system uses the same UART resource and repeats randomly sent packages. Or, for example, if a driver is built into the kernel, an attacker recompiles the kernel by rewriting the UART driver code in another way. The final application does not know what is happening.

What is secure boot and why is it needed?


The first level of protection for your software on the target system is secure boot. Hereinafter, secure boot is explained for processors of the i.MX6 family.

Secure boot in i.MX6 is implemented through High assurance boot (HAB) and can perform two functions:

  • secure boot
  • encrypted download.

In this article, I will describe the process of ensuring safe processor loading. I.MX6 also implements an algorithm that provides bootloader encryption. However, its use requires the generation of a unique bootloader for each product, since it uses a unique encryption function for each processor. This is an inconvenient procedure in the series, so I did not describe it. If you have anything to say on this topic, I will be glad to hear.
Safe boot tries to ensure that the bootloader starts on your product, with the kernel you loaded into it.

About the definition that I gave secure boot. Secure boot does not save you from copying the product. Yes, this makes the procedure more difficult, but not by much. The bootloader cannot sign the entire FS, therefore it does not provide protection at the OS level and at the level of the final application. It all works classically: through asymmetric encryption. I will describe the process on the fingers; it can be read more deeply in [4]. The bootloader indicates the script for the HAB, where the signed memory areas are indicated, and where the digital signature lies. The public key is pre-located in the One time programmable (OTP) processor memory. When loading, the script checks the specified memory area using the attached digital signature and a wired key. If the test fails, the processor does not start the download. HAB operation must also be activated by setting a bit in the OTP memory region.

Disadvantages of secure boot:

  • If the HAB is not activated, the firmware will still start. This means that without add. protection methods your firmware will run on a copy of the product. In order to prevent this from happening, the bootloader must independently activate the HAB at startup. But then by inserting a USB flash drive into another product and forgetting to flash the public key, you can throw the processor out.
  • When accessing the processor memory, you can copy the public key, this is enough to copy the product.

Implementation


In general, there are quite a lot of English-language articles [5] [6] on the Internet on the implementation topic, but not a single proposed method took off at once, and links to the software started to die, so I decided to save this as an article primarily for myself, when I will return to this issue in the future.

First you need to download the following software: code signing tool [7] and the necessary scripts for generating keys and signing. In the specified archive, I removed all unnecessary utilities and libraries, since there are quite a lot of unused for our task.

Next, you need to generate the keys:

cd ${CST_PATH}/release/keys
echo ${serial} > serial //8 ,     
echo ${password} >  key_pass.txt //   ,     
echo ${password} >>  key_pass.txt //
./hab4_pki_tree.sh //     
Do you want to use an existing CA key (y/n)?: n 
Do you want to use Elliptic Curve Cryptography (y/n)?: n
Enter key length in bits for PKI tree: 4096
Enter PKI tree duration (years): 10
How many Super Root Keys should be generated? 4
Do you want the SRK certificates to have the CA flag set? (y/n)?: y
 ../linux64/bin/srktool -h 4 -t SRK_1_2_3_4_table.bin -e SRK_1_2_3_4_fuse.bin \
 -d sha256 -c ./SRK1_sha256_4096_65537_v3_ca_crt.pem,\
./SRK2_sha256_4096_65537_v3_ca_crt.pem,./SRK3_sha256_4096_65537_v3_ca_crt.pem,\
./SRK4_sha256_4096_65537_v3_ca_crt.pem -f 1

The key generation script raises the above questions. I gave an example of standard options. The answer to them determines how the generated keys and parameters of the generated keys will be called. Consider with further commands. Next, we read the public key necessary for writing to the processor. We will use a convenient script from [5] for this .

cd ${CST_PATH}/release/linux64/bin
./var-u-boot_fuse_commands.sh 
fuse prog 3 0 0xFFFFFFFF //   uboot.
fuse prog 3 1 0xFFFFFFFF //     
fuse prog 3 2 0xFFFFFFFF //       .
fuse prog 3 3 0xFFFFFFFF //  -   .
fuse prog 3 4 0xFFFFFFFF //      .
fuse prog 3 5 0xFFFFFFFF //
fuse prog 3 6 0xFFFFFFFF  //
fuse prog 3 7 0xFFFFFFFF //

This task can be applied to uboot with integrated SPL or with separate SPL and uboot (rocko release for example). I am writing a release so that you know where they are used as standard. If you compile uboot yourself, then most likely your SPL will be integrated into uboot. An important point in the defconfig bootloader is to add the following options:

CONFIG_SECURE_BOOT=y
CONFIG_SYS_FSL_HAS_SEC=y 
#define CONFIG_SYS_FSL_SEC_COMPAT    4 /* HAB version */
#define CONFIG_FSL_CAAM
#define CONFIG_CMD_DEKBLOB
#define CONFIG_SYS_FSL_SEC_LE
#define CONFIG_FAT_WRITE

These options activate drivers for working with hardware modules. Everything is like in menuconfig for Linux. When compiling the bootloader, use verbose mode (remove V = 1. The assembly with this parameter is written in the guide. However, I cleaned it myself). In this case, information about the address of the entry point and other things necessary for recording a digital signature is added to the compilation logs. After compilation, we need the following output files:

  • u-boot-ivt.img
  • u-boot-ivt.img.log
  • SPL
  • SPL.log

copy them to $ {CST_PATH} / release / linux64 / bin. Next, you need to run a fairly simple script.

Detailed description of the script procedure
(*.csf). , :

  • .

. , ( ).

:

Image Name:   U-Boot 2019 //  U-boot
Created:      XXXXXX
Image Type:   ARM U-Boot Firmware with HABv4 IVT (uncompressed)
Data Size:    339904 Bytes = 331.94 KiB = 0.32 MiB
Load Address: 17800000
Entry Point:  00000000
HAB Blocks:   0x177fffc0   0x0000   0x00051020

Image Type:   Freescale IMX Boot Image //  SPL
Image Ver:    2 (i.MX53/6/7 compatible)
Mode:         DCD
Data Size:    61440 Bytes = 60.00 KiB = 0.06 MiB
Load Address: 00907420
Entry Point:  00908000
HAB Blocks:   00907400 00000000 0000cc00
DCD Blocks:   00910000 0000002c 00000004

«HAB Blocks». ,

cd ${CST_PATH}/release/linux64/bin
./cst --o u-boot_csf.bin --i u-boot.csf
cat u-boot-ivt.img u-boot_csf.bin > u-boot_signed.img


As far as I remember in the case of integrated SPL, most likely everything will work the same way, but it's worth checking it out. I don’t remember now and didn’t begin to compile when writing this article.

cd ${CST_PATH}/release/linux64/bin
SOC=mx6 ./var-som_sign_image.sh SPL u-boot-ivt.img

According to the results of the script, you will get two files: SPL_signed, u-boot-ivt.img_signed, which must be written to the disk of the target system. I apply an example of a recording script to the target system. Without sudo, so as not to accidentally shoot you in the leg.

dd if=SPL_signed of=${DEVICE(/dev/sd*)} bs=1K seek=1; sync
dd if=u-boot-ivt.img_signed of=${DEVICE(/dev/sd*)} bs=1K seek=69; sync

Having written to a blank medium, you should get a bootloader without a kernel that will crash into the console, since it will not find dtb and the kernel. In the console, you can check whether what you have done or not really works. In the presence of event - something went wrong. Important: if the bootloader is compiled without HAB support, there will be no errors either.

hab_status
HAB Configuration: 0xf0, HAB State: 0x66
No HAB Events Found!

The last step is to activate the HAB, attention is a one-time command, it cannot be fixed, so be careful

fuse prog 0 6 0x2

Conclusion


I did not begin to describe the process of integrating all this into yocto, but I have to keep secrets. However, this is feasible and does not require much time.

reference


[1] bare metal- embedded software that directly runs on hardware without any abstraction in the form of operating systems
[2] we.easyelectronics.ru/Soft/zaschita-ustroystva-ot-vzloma-i-kopirovaniya.html
[3 ] habr.com/en/post/350602
[4] HAB4_API.pdf at github.com/BMValeev/CST_TOOL
[5] variwiki.com/index.php?title=High_Assurance_Boot
[6] boundarydevices.com/high-assurance-boot -hab-dummies
[7] github.com/BMValeev/CST_TOOL

All Articles