Background: The embedded system has a bootloader that is used to update the application code. The application code is an encrypted hex file that will be sent to the bootloader through UART from a computer and the bootloader decrypts the message. We are considering a simple symmetric key encryption.
Sometimes, we may need to update the bootloader. The bootloader update will be an unencrypted HEX and we will send this HEX file to the end user so they can update the bootloader. There is chance the user can view the HEX file and figure out the encryption key. So we don't want to store the encryption key in the source code.
Is there any technique to send an encryption key (not as plain text) to the embedded system first and then send the encrypted message (application hex)? Or any technique so that the bootloader can "figure-out" the encryption key but other people can't figure out the key?
Microcontroller is an arm cortex M0.
Edit: Thanks for the replies. A bit more details that I didn't think was important at the start but it is now.
The hardware will not be manufactured by us but by our user (say user^1). The bootloader was made so that the user's user (say user^2) can update the application later on. So the bootloader is flashed by our user (user^1) using the ARM SWD (which is just the programmer kit for the microcontroller).
When the bootloader runs for the first time, it enables the readout protection through the in-built API in the microcontroller. So once the bootloader is flashed, it is safe to assume that nothing from the Microcontroller can be extracted or overwritten using the debugger. (we have checked this)
I should also add that the bootloader update involves clearing the flash and making the hardware as good as new. The user^1 then follows the same steps as before.
Since we don't manufacture the board, this makes it difficult to
- encrypt the bootloader as the programmer kits don't support encrypted flashes.
- Store the encryption key in separate address as we are not making the hardware.
The goal finally is to only protect the application HEX from being extracted as we are only charging for the HEX (or executable).
-
\$\begingroup\$ It sounds like the application update is encrypted so you already have firmware capable of decrypting an update. Why not encrypt the bootloader update? Consider storing the crypto key as another chunk of data separate from both the bootloader and application. \$\endgroup\$kkrambo– kkrambo2025年06月03日 15:00:59 +00:00Commented Jun 3 at 15:00
-
\$\begingroup\$ I have edited the question, sorry I didn't include the details earlier. We don't manufacture the hardware. \$\endgroup\$student_EE– student_EE2025年06月03日 15:51:32 +00:00Commented Jun 3 at 15:51
-
\$\begingroup\$ Does your customer (or your customer's customer) get to use unlimited copies of your application? You're charging for the application, but encryption does not prevent copying. \$\endgroup\$spuck– spuck2025年06月04日 23:06:41 +00:00Commented Jun 4 at 23:06
-
\$\begingroup\$ @spuck Yes our customers (user^1) get to use unlimited copies of the app. We charge one time for the app handover (give them the hex file) and charge one time for each update handover (give them new hex file). How (user^1) charges (user^2) for apps and updates we don't know as that can change based on (user^1) and (user^2) contract. \$\endgroup\$student_EE– student_EE2025年06月05日 06:25:37 +00:00Commented Jun 5 at 6:25
-
1\$\begingroup\$ So you don't trust your customer? I'm trying to understand what you're protecting with the encryption. Your app is proprietary and you're trying to prevent your customer from reverse engineering it to make updates without paying you further? \$\endgroup\$spuck– spuck2025年06月05日 15:34:57 +00:00Commented Jun 5 at 15:34
4 Answers 4
To answer your question
Is there any technique to send an encryption key (not as plain text) to the embedded system first and then send the encrypted message (application hex)? Or any technique so that the bootloader can "figure-out" the encryption key but other people can't figure out the key?
No, there's no such technique. During the initial key programming (it doesn't matter is the key embedded in the bootloader binary or is it stored in a separate security chip e.g.) the key will be visible as plain text.
As you don't have any manufacturing on your own, it is a matter of choosing the third party who you trust the most (or distrust the less). You can order preprogrammed chips from the chip manufacturer and provide them unencrypted bootloader binary (containing the key), if you trust them more than your user^1. For later bootloader updates, you need to provide encrypted bootloader binary, which you can receive and decrypt with currently programmed bootloader and the key.
Edit:
You can, however, mitigate the issue:
You can order chips with preprogrammed bootloader and the first key, which will be used only to decrypt and store the second key programmed by the user^1. Only the second key will be able to decrypt the application binary.
This way the chip manufacturer will know the first key, but it is not good for anything without the second key. The user^1 will have the second key, but encrypted, so no good for anything either without the first key. The attacker would have to acquire both keys in order to decrypt the application binary.
-
\$\begingroup\$ yes it looks there is no such technique and this is a pretty direct answer to my question. As for your edit, we can't order chips for our customers. But we will make note of this for our future projects. \$\endgroup\$student_EE– student_EE2025年06月04日 13:14:07 +00:00Commented Jun 4 at 13:14
Sometimes, we may need to update the bootloader. The bootloader update will be an unencrypted HEX and we will send this HEX file to the end user so they can update the bootloader. There is chance the user can view the HEX file and figure out the encryption key. So we don't want to store the encryption key in the source code.
Then simply don't make the key part of the bootloader, but store it in a separate location.
The application code is an encrypted hex file that will be sent to the bootloader through UART from a computer and the bootloader decrypts the message. We are considering a simple symmetric key encryption.
It's "game over" if you allow for the bootloader to be overwritten by unauthenticated software. Then the user can just upload their own bootloader and read all memory.
All in all, your whole systems seems badly thought through:
- Firmware needs to be first and foremost be authenticated, not only encrypted. These are two different things.
- Your bootloader must under no circumstances allow for replacement of itself by an unauthenticated piece of software
- symmetric encryption seems to be the fundamentally wrong choice here: the usual method here is to have an assymmetric key in the device, which can be used prove authenticity of an update and to decrypt a symmetric key into RAM that's sent as "preamble" to your update for quicker of the actual firmware, if necessary.
You also seem to have a misconception what "hex" means (I think you mean a file that you typically view in a hexadecimal viewer...), but that's a different problem.
Microcontroller is an arm cortex M0.
Make sure your microcontroller choice supports disabling the debug interface or has a secure element with memory that's unreadable (aside from the element itself) for keys. Else, all this is moot, as anyone could just attach a debugger and read the keys.
-
1\$\begingroup\$ I have edited the question with some more details. As for overwriting the bootloader by unauthenticated software, we don't really mind as the only way to overwrite the bootloader is by first erasing the flash and by that time, our hex file is also erased. The goal is to only protect the application HEX file. \$\endgroup\$student_EE– student_EE2025年06月03日 16:44:20 +00:00Commented Jun 3 at 16:44
-
1\$\begingroup\$ Even if malicious code enters the hardware through our existing bootloader, the application is erased before the malicious code is flashed and our goal is achieved (protecting application HEX). Our goal is not to protect the hardware or prevent abuse of the hardware. We only charge for the application executable (HEX) and we want to ensure the unencrypted hex file is not available easily to others. \$\endgroup\$student_EE– student_EE2025年06月03日 16:58:48 +00:00Commented Jun 3 at 16:58
-
1\$\begingroup\$ and an application executable is really not "a hex". \$\endgroup\$Marcus Müller– Marcus Müller2025年06月03日 17:41:21 +00:00Commented Jun 3 at 17:41
-
1\$\begingroup\$ Once "our" bootloader runs, it enables the read-out protection through the official microcontroller API. This also includes protection from writing to flash through debugger. The device is "locked". And the microcontroller ensures that if the read out protection is disabled later ("unlocked"), the flash contents are erased completely. So it is unlikely that "our" bootloader code can be replaced by malicious when our application code exists in flash. This is fine for us, as we only want to keep the decrypted binary safe from others. \$\endgroup\$student_EE– student_EE2025年06月04日 03:11:56 +00:00Commented Jun 4 at 3:11
-
2\$\begingroup\$ @student_EE but you need to give your bootloader to your customers first, so that's not true – everybody can decrypt things, because you're shipping the symmetric key to them. So, this won't work – no matter how much you bolt onto the concept, you cannot rely solely on symmetric encryption here. You need a hardware anchor of trust, and you need to use encryption that doesn't rely on the same secret you explicitly shipped to your customer. \$\endgroup\$Marcus Müller– Marcus Müller2025年06月04日 12:26:34 +00:00Commented Jun 4 at 12:26
If you still have an option to change MCU, one possible solution would be to use device with dual bank flash. Then you can encrypt bootloader itself and upload it using previous bootloader which will switch banks after decrypting and authenticating a new one. You can also find device with hardware AES, simplifying the code and reducing its size.
As a variant of the above, you can embed bootloader functionality into the application itself, then you only have one binary to distribute to the users.
Having said that, I am yet to see a case where bootloader needs field updates. This is usually the component that is thoroughly checked before going into production, and its function does not change no matter what you do with application.
-
\$\begingroup\$ note that bootloaders are often small enough to fit in RAM and still leave space for verifying another one :) \$\endgroup\$Marcus Müller– Marcus Müller2025年06月03日 22:31:27 +00:00Commented Jun 3 at 22:31
-
\$\begingroup\$ @MarcusMüller good point. the handover and rollback will be a pain though, both easily solved by dual banks. \$\endgroup\$Maple– Maple2025年06月03日 23:37:34 +00:00Commented Jun 3 at 23:37
-
1\$\begingroup\$ I didn't do a good job of describing the problem... We don't make the boards. we only sell the final executable. So at some point, our user will have to load the bootloader into their hardware. This loading was mistakenly called "update" but it drives the point in that the bootloader hex is visible and gets loaded into the hardware by someone other than us. In this situation, we want to ensure app hex is unavailable. So if app hex is encrypted, and the bootloader decrypts the hex during update, it will work. The issue is we don't want to store the key in bootloader code. \$\endgroup\$student_EE– student_EE2025年06月04日 03:00:06 +00:00Commented Jun 4 at 3:00
If you are able to do a bit more of a handshake, and e.g. using an STM32 you could use the unique serial number of the MCU in generating a public key for that device, send the public key to the external configurator, encrypt on the fly using that key and send it back to the MCU, where a key decryption takes place before flashing the code.
With code-readback protection applied to the bootloader flash sectors to hide the algorithm which is generating keys derived from e.g. the serial number or even a noise based random number generator present in some MCUs.
It might not need to be a monster cryptographic algorithm to do this, just enough to make things seem random on the link between the two devices.
On these MCUs depending on model, you can often soft reset into a configuration that is running code from RAM, and have a tiny bootloader in RAM calling utility subroutines in a ROM library that can reflash the secure bootloader, or soft reset into the built in bootloader to play yet more tricks.
You do not need to erase the entire flash, only the erase blocks which you are re-flashing, so code routines can be stored in another erase block, copied into RAM and executed there to achieve re-flashing of erase blocks. Some MCUs have two flash banks so all that happens is one bank goes offline while being written to.
-
\$\begingroup\$ Interesting idea. When you say: encrypt on the fly using a public key generated from the MCU" (paraphrasing), that means the app HEX is unencrypted in the PC GUI (did you mean configurator here?) and the PC GUI encrypts the app using the public key that the MCU sends and transfers the encrypted app back to the MCU by UART (correct?). In this case, the app is still unencrypted in the GUI and that causes a problem right? Or am I misunderstanding what you said. \$\endgroup\$student_EE– student_EE2025年06月05日 06:58:11 +00:00Commented Jun 5 at 6:58