In “5 Elements to a Secure Embedded System – Part 2 Root-of-Trust”, we continued our discussion about the five essential elements required to begin securing an embedded system. As you may recall, the five elements that every developer should be looking to implement are:
- Hardware-based isolation
- A Root-of-Trust (RoT)
- A secure boot solution
- A secure bootloader
- Secure storage
The main focus last time was that the system needs to have a Root-of-Trust which forms the foundation for a chain-of-trust as the system boots and executes its application code. In today’s post, we will continue the discussion with secure boot.
Element #3 – Secure Boot
Secure Boot is the process of validating code and images against hardware before they are used. The idea is that the system should not trust any code or image until it has been validated as being authentic and that its integrity is confirmed. The system, as mentioned, should use a hardware-based Root-of-Trust (RoT) to validate the code. Many systems that I’ve encountered in my career simply assume that the code in their flash memory is correct and start executing it the moment the processor boots with few, if any checks. If a developer wants to secure their system, this is one practice that can longer be followed. The system needs to securely boot and validate all the code that it will be loading and executing.
Booting from a Root-Of-Trust
Secure boot is probably the first element that we have been discussing that utilizes both hardware-based isolation and RoT. In the first phase, ROM code which establishes the RoT is used to calculate a hash over the contents of the boot code and verifies its contents. The calculated hash is usually compared with a hash that is immutable and has been “burned” into the flash during manufacturing. The initial secure boot phase will have configuration information built-in that then allows the boot sequence to validate subsequent boot phases.
For example, this phase may then go through and calculate a hash over the bootloader and compare that hash to yet another one stored in flash that helps to establish trust in that phase. The bootloader can then be validated and executed which will then go through and validate its application image before running the application. An example process can be seen below:
Source: The Beningo / Cypress security training course: Getting Started with Secure Device Management using PSoC 64 and AWS
Secure Boot Stages
Now, secure boot as you can see happens in stages, but secure boot also often happens in isolated memory regions. A secure system will often first boot into an area of memory that is considered to be a secure execution environment. There are several different ways to do this. One way is to use a multicore microcontroller where one core is dedicated to security features and code execution and the other is allocated to application-rich code. In this setup, interprocessor communication (IPC) is used to communicate between the cores.
Alternatively, developers can use a single core technology that separates memory and the execution environment into secure and non-secure environments. An example is Arm TrustZone. With TrustZone, developers are able to specify which memory regions should be allocated to secure processing and which are not. These regions not only include code but also peripherals, flash, and RAM memory. The application will run its execution-rich code and when it needs to make a call to a secure function, the processor transitions to a secure mode that is isolated and allows the secure functionality to be carried out. This helps to ensure that secure information such as private keys, data, and so forth is inaccessible should a hacker gain access to the feature-rich execution environment.
When using secure boot in an isolated environment, the secure processing region is often the first to start executing. In other words, secure boot operations are executed from the secure execution environment, which makes sense. Once the secure environment is up and running, yet another hash is calculated over the application-rich code to ensure its integrity and authenticity. After it has passed this check, the secure execution environment will then allow that code to run.
At each point in the boot sequence, the secure boot must authenticate the next code or image that is going to execute and verify its integrity. Starting from the RoT and then working its way through the bootloader and to the execution image. In a multicore system, there may even be multiple images that need to be verified across multiple processors.
Secure boot is much more than just simply adding a piece of software to the system. Secure boot requires a developer to carefully think through the boot sequence and develop a Chain-of-Trust that originates at the Root-of-Trust. As the system boots, each image and code are verified, and only afterward is it allowed to execute on the system. If a problem is detected, the system can either halt the boot sequence or maybe even revert to an earlier known working version of code. In order to revert the code, or update it to a new version, that requires the system to have a secure bootloader.
In the next part, we will discuss secure bootloaders, what it is and what it means to developers.