With the proliferation of Internet of Things (IoT) devices, which now span just about every walk of life, from smart cities to wireless jewellery, the need to prioritize security in IoT-style embedded systems has never been greater. The secure boot process is a vital first step in securing any embedded system, a necessary part of your application’s anti-malware fortress. Let’s take a look at the pros and cons, with a focus on one of the most popular processors in electronics – the i.MX6.
What is secure boot?
Secure boot is a process where your OS boot images and code are authenticated against the hardware before they are allowed to be used in the boot process. The hardware is set up beforehand in such a way that it only authenticates code generated using security credentials you trust. In short, it ensures that the boot and OS software is the intended manufacturer version and hasn’t been tampered with by malware or malicious third parties.
Secure boot is applicable for any single-use device, a good example being an e-reader, a popular use of the i.MX6 processor (the i.MX6 Solo and DualLite have an integrated E-Ink display controller, for example), which is intended for specifically reading e-books, rather than general computing. Having a locked-down Linux environment at boot is useful in this case.
Other situations, such as an Android phone, may have trade-offs. Using secure boot would restrict end users from running custom ROMs, for example. Being able to do this may be a feature, or may be desirable based on product placement or security requirements. Essentially, a good time to use secure boot is any case where you don’t want another party to load an operating system or a different bootloader onto your device.
For more integrated systems such as IP cameras running Linux, you would be well advised to use secure boot, as any malicious boot code or operating system software could lead to a situation where the device is made part of a botnet. Or potentially the feed from the camera could be publicly uploaded onto the Internet or otherwise altered so that the feed does not contain the footage wanted by the owner.
Secure boot process on the i.MX6
On the i.MX6, once you have created your boot images, in order to utilize secure boot you would generate a set of secure keys against an SSL certificate generated for this purpose.
These keys are used in the generation of a secure set of commands which are compiled and appended to the boot image using vendor (Freescale, now NXP) -supplied tools. The processor will then take your first stage bootloader and authenticate the certificate data generated by the secure boot compilation tools using your certificates.
If the key data in your boot image that’s written to your boot media matches the key data stored in the secure storage in the processor, the secure commands will be executed, which will then check a cryptographic hash of your images to make sure they match what the secure commands say they should be. If they do match, the processor will load and execute your boot image.
Once this process has passed through the CPU's internal bootloader, you can still call into the secure boot library from your bootloader code. This allows you to load your operating system image and authenticate it in the same way that the CPU bootloader authenticates your software bootloader.
At the end of this process, the OS has booted in a verified secure environment. You know that it’s legitimate because every stage has passed the test of being authenticated against the key hashes that are in the processor.
Underlying this process from a security perspective, root keys are generated from your SSL certificate, hashed, and burned into the CPU in a one-time programmable process. Once that key has been burned into the processor, it cannot be changed – one of the reasons it is secure.
Your boot images are also signed against this key and the data generated from this signing process is combined with your image. The processor checks your image key with its key and, if those match, it checks the image against the key that was just matched with the processor. If that matches, the image is executed. This takes you up the chain, from CPU bootloader to a normal bootloader to the operating system.
Of course, this is i.MX6 specific and there are different flavors of secure boot, such as X86 with UEFI secure boot, but we’ll stick to i.MX6 for clarity here.
Leveraging the hardware
The i.MX6 hardware suite has a number of specific security mechanisms that are of benefit here. The key part of these for secure boot is the one-time fuses used in burning your key. Once they’re blown, they cannot be unblown, so once you’ve burned your key, your key hash is permanent. Multiple keys can be combined into one key hash as well, so you can revoke a key if it has been compromised.
Another function of system security is the CPU internal bootloader, a static piece of code which has also been security tested. This is a key foundation in maintaining a chain of security up to the operating system level.
In addition, the i.MX6 has a hardware cryptographic algorithm accelerator. Algorithms such as AES hashing, Triple DES hashing, SHA1, SHA256, can be accelerated by the processor, which speeds secure operations considerably.
The most obvious pitfall is that you are responsible for your own security. If your keys leak out into the world, someone can sign that code against the key that’s in the processor anyway, so you need to keep your process secure, along with the hardware.
In addition, a processor configured to only boot securely will not boot unless it has an image that is signed properly. So, any errors in burning your hash into the processor may result in a processor that can no longer run code as the burnt-in hash does not match – effectively a useless processor.
Once the processor is set to be secure, there is no way of loading unsecured code to it. It will only allow you to securely load from storage, such as SD or NAND flash, or other modes of loading software into the processor (USB image loading).
Therefore, the steps for preparing your hardware and the processor must be kept secure. You must also ensure that your boot loader is prepared for this as well.
As previously mentioned, your bootloader must call into the secure boot library on the processor in order to authenticate the next stage in the boot chain. If you have not properly coded your bootloader for the proper use of the secure library, then you may not fully ensure that your OS is secure.
Don’t be lulled into a false sense of security
To be clear, secure boot on the i.MX6 doesn’t lock the entire system down, only the OS software that’s on there. So, someone might write some Linux malware that runs on top of the OS which, if they successfully load, could compromise the system.
i.MX6 Secure boot authentication
It is possible to authenticate parts of the file system and other code, if you require full security. The secure boot process on the i.MX6 works on the principle of specific chunks of memory having a specific cryptographic hash and associated signing information. This makes it possible to load the OS's root file system and other key files into a fixed point in memory, along with the correct set of secure commands. This would allow you to authenticate other parts of the system if necessary.
Top tips for secure boot on the i.MX6
1. Make your process secure
Once you’ve decided you need to go down the route of secure boot, make sure the surrounding processes are up to scratch too. Leaking your keys out of the production environment undermines the point.
2. Keep your encryption strong
Make sure you keep your encryption strong. It’s possible to create very weak keys and secure boot on the i.MX6 supports some older, now regarded as compromised, combinations. Make sure your algorithms are up to date and fit for purpose.
3. Check your code
In order for secure boot to mean anything, the rest of your code in the bootloader, OS and other software also has to be properly written for secure boot, and lack security holes.
In addition, each stage of the boot process must check the next before executing it. If this is not done or only part-way done, there is much less scope for calling the process secure.
4. Authenticate everywhere
For genuine security, authenticate as much of the code you want to load as possible and do ensure that it follows the practices established for the libraries.
You need to secure the process, i.e. how you generate and store your keys. Secure boot can only check the signing, and any signed image can be considered secure by the processor.
Make sure that each separate part of the code you’ve written is called into the secure boot library in the processor. You call into that library to continue authenticating your images because most i.MX6 boards have a multi-stage boot process, where you have the CPU internal boot loader, which loads an SPL which, in turn, loads a full boot loader that loads the OS. Each of these jumps needs to be authenticated by the preceding step to be considered secure.
5. Confirm the process is authenticating correctly
It’s essential to ensure that your code is genuinely performing the secure boot. It is possible, that even from a secure piece of code, to jump to an arbitrary location in memory and continue execution because that’s just how processors work. It is essential to actually ensure your code is authenticating the next step of code in order to maintain security.
The most common bootloader used for the i.MX6 is U-Boot, which does support secure boot on the i.MX6. It needs to be configured but it’s a lot easier, and there are fewer ways to get it wrong when a lot of the work has already been done for you. Writing the security from scratch is a good way to not do it right – it’s much better if you take a known, good implementation and match it to your needs.
Nathan Padoin is an embedded software engineer at ByteSnap Design in the UK. After gaining his computer science degree at the University of Southampton, Nathan joined ByteSnap Design, an electronics design consultancy specializing in embedded systems development encompassing hardware and software design. Nathan works on embedded systems projects – control systems and multimedia devices, amongst others – across several markets, such as industrial and AI. His expertise includes Linux and Android BSP development.