In this article we discuss the application of code signing to an embedded system and we use a smartphone as the example system. AP, Application Processor, is the CPU in a smartphone and main software such as iOS and Android runs on top of it. It controls multiple “peripheral” SoCs such as cellular modem SoC, WiFi SoC, etc. To lower the cost of “peripheral” SoCs, normally they are not assigned with NVMEM, nonvolatile memory. NVMEM is only available on AP side. So when system boots up, AP needs to download modem SoC firmware image from AP’s NVMEM to RAM inside modem SoC and then let modem processor run the download firmware image out of RAM.
The issue is an attacker can alter the downloaded fw image with malicious code. This poses a big threat to the safety and integrity of the smartphone.
So how do we detect an altered or corrupted software? This is where code signing kicks in. The fundamental idea is shown as below. AP side needs to sign the code first. It is a two step process. First it uses hash function to calculate the digest of the code to be signed. As shown in the diagram, in addition to real fw code, we also need to include certain overhead such as TS which stands for time stamp and ID which stands for SoC ID. Therefore what is to-be-signed is real fw plus the overhead. Digest calculation is like CRC and it outputs a 128bit or 256bit digest based on the SHA function used. This digest is then “signed” with a private key and generates signature.
Each time AP wants to boot up modem SoC, the overhead, code, and signature are then downloaded to modem SoC side. Modem SoC first uses the same hash function to calculate digest of overhead and code. It then uses a public key to generate a local signature out of the digest. This local signature is compared with the downloaded signature. If they match, fw image is signature verified and modem SoC processor is safe to run the fw image. If it fails, mode stops and flags error to AP.
There is a good tutorial about Code Signing and Verification with OpenSSL. Code, script, and commands needed for code signing with OpenSSL are provided in this tutorial. The same concept applies to code signing in embedded system too.
Code signing is closely related to modem SoC boot process. One implementation is to design a dedicated hardware DSA, digital signing accelator. AP first powers up modem SoC but modem processor is blocked from running. AP downloads fw image to modem SoC RAM and then starts HW DSA to sign-verify the download fw image. During sign-verification, HW DSA needs to read public key out of local ROM. It is possible to save public key in OTP, one time programmable memory. If verification is passed, HW DSA starts modem processor to run the downloaded image.
Another implementation is to let modem processor and therefore the firmware to take care of code signing. As before, AP needs to power up modem SoC and download image to RAM. Then modem processor starts to do code signing. Modem processor needs to fetch code for code signing out of ROM. What is left is the same. If verified, modem processor jumps to the downloaded image and starts executing it.
There are some considerations when implementing code signing in real embedded system.
- It is important to protect modem SoC RAM once firmware image is downloaded and code-verification process starts. Otherwise during verification process, an attacker can alter RAM content after DSA or firmware processes it.
- It is also important to tun off modem processor debug function during code verification. In general, modem SoC jtag function needs to be turned off.
- Code signing can be combined with code encryption.
- For simplicity, above uses one private-public key pair. In reality, an embedded system needs to support multiple pairs. For example, we need to separate development key pair from production key pair. For production key pair, we may need to support multiple pairs for different customers so each customer can have their own private key.
- How to bypass code signing in development phase and debug phase. In development phase it is possible to use a specially packaged chip which has some pin bonded out. This pin can be used to disable/bypass code signing. On production packaged chip this pin is not bonded out so attacker won’t be able to compromise. If needed to bypass code signing in production packaged chip it is possible to use OTP, one-time programmable memory. But it poses question how to protect OTP.
- For the 2nd implementation that modem processor needs to fetch code out of rom to verify signature, special care needs to be given to rom patching. Rom patching is commonly used to patch rom code in field to avoid costly chip respin. However it opens the door for an attacker to maliciously alter rom code to compromise code signature verification.