Raspberry Pi’s Novel “Security through Transparency” Approach
Raspberry Pi’s RP2350 MCU has more memory, faster processors, and lower power states compared to its predecessor, the RP2340. In this month’s article, Colin explores some additional features. He emphasizes transparency, enhanced security with its innovative boot ROM and redundancy coprocessor, and glitch protection. He then shares the results of Raspberry Pi’s “hacking challenge” to identify and fix security vulnerabilities in the chip.
Recently, Raspberry Pi introduced the RP2350 microcontroller, which has a huge amount of features under the hood. If you’ve used the older RP2040, you know it was already an interesting device, with unique features such as a high-speed “Programmable In Out” (PIO) block that allowed you to effectively bit bang high-speed protocols using a special-purpose peripheral block. But the RP2040 looks tame compared to the RP2350. One of the most ambitious features is that the entire architecture of the device can be changed. The RP2350 can be booted in two modes, a dual-core Cortex-M33 (ARM Cortex) mode, or a dual-core Hazard3 (RISC-V) mode.
One of the main features missing from the RP2040 was code security. The device booted off an external SPI flash, which meant that anybody could easily read out the flash memory. Making a secure system primarily meant using an external device that provided a secure boot, but adding an external device negated the low-cost advantage of the RP2040.
The RP2350 also fixes that problem, and as I’ll discuss in this article, it introduces all sorts of novel features that haven’t previously been available in a low-cost microcontroller. Unlike many “secure microcontrollers,” Raspberrry Pi is keeping the public informed about challenges in their security, and has already published a series of blog posts about attacks on their first step of the microcontroller. At the end of the article, I take a look at known attacks on the RP2350.
RP2350 SECURE BOOT
To begin, I’ll describe the basic secure boot flow of the RP2350. When discussing this MCU, I also have to emphasize that one of the most unique features is just how open everything is. The source code for its boot ROM can be found on Raspberry Pi’s GitHub page [1]. Normally we’d have to rely on the manufacturer to describe the boot flow, and we’d have to trust their description. But for this device, you can also look at how the boot ROM was designed, not just trust a vague description in the manual.
Fundamentally, the fact that the RP2350 includes One Time Programmable (OTP) memory is what makes the device able to be secured. The existence of OTP alone does not mean a device is secure, but it can form the basis for a secure boot.
The boot ROM of the RP2350 uses the OTP to allow you to force it to boot authorized images. This can be configured to force images to pass a signature check, for example. Additionally, the configuration offers more advanced features, such as anti-rollback—which allows you to ensure someone cannot make the device boot an “old” firmware image after you already fixed security vulnerabilities.
Of course, such boot ROMs would normally be vulnerable to a “fault injection attack.” I covered this in previous articles, but I point you especially to my September 2018 article in Circuit Cellar, “Embedded System Essentials. Recreating Code Protection Bypass: An LPC MCU Attack” [2].
As an example of some of these features, you can see a block of code in Listing 1, which I’ve taken from the arm8_bootrom_rt0.s function of the boot ROM. It includes some instructions that aren’t your normal Arm assembly: rcp_canary_get and rcp_canary_check. These instructions are used to catch attacks where the function returns (with the bx lr on the final line of Listing 1), without having originally entered this function. This will catch a few types of typical attacks:. One of them would be Return Oriented Programming (ROP), where you corrupt the stack to build your own program, by using the return function in each subroutine to jump to different sections of code. If you jump to the middle of a protected subroutine, when the rcp_canary_check is done before the return, the RP2350 will throw an exception instead of allowing the return (bx lr) to occur.
Listing 1
A block of code that includes instructions to catch attacks.
s_varm_step_safe_reset_unreset_block_wait_noinline: rcp_canary_get ip, CTAG_S_VARM_UNRESET_BLOCK_WAIT_NOINLINE ldr r1, =RESETS_BASE + REG_ALIAS_SET_BITS str r0, [r1, #RESETS_RESET_OFFSET] ldr r1, =RESETS_BASE + REG_ALIAS_CLR_BITS str r0, [r1, #RESETS_RESET_OFFSET] // Remove alias bits (note we’re avoiding v8-M Main instructions here) lsrs r1, #14 lsls r1, #141: ldr r2, [r1, #RESETS_RESET_DONE_OFFSET] bics r0, r2 bne 1b rcp_canary_check ip, CTAG_S_VARM_UNRESET_BLOCK_WAIT_NOINLINE bx lr
But how exactly is it doing this, and what are these “RCP” instructions? The answer is that there is a special block called the Redundancy Coprocessor (RCP) that adds a lot of security into a special hardware block. Lets look at this in more detail.
REDUNDANCY COPROCESSOR (RCP)
The RCP is a unique block in the RP2350 that makes it easier for you to write more secure code. As I mentioned as one example, it has an ability to check that program flow is proceeding as expected. But it has a lot more capabilities as well, so I want to talk in more detail about the RCP and why you should find it especially interesting.
To begin, the RCP requires some random number to ensure you have unpredictable values on every program run, called the “salt register.” For the example of the stack canary commands in Listing 1, those commands write known-but-random values to the stack. In this case, “known but random” means it’s a value based on the fixed (known) value of the function ID that you define, and the random salt value. The RCP can check that the expected value is present on the stack before returning from a function, and if not, an unmaskable exception occurs.
But this is just one feature of the RCP. To introduce the rest, I want to bring back some code I’ve used previously, which is shown in Listing 2. This was an example of an unsecure bootloader implementation. This pseudo-code (which looks like C) is designed to check that a firmware image variable (stored in firmware_image) has an acceptable signature. If it does, it boots the image, and if not, it blocks the boot and waits for the user to reset the system and load a new image. This sort of logic is used in many real bootloaders, but I’m avoiding picking on any vendor here.
Listing 2
Example of an unsecure bootloader implementation.
if (check_signature(&firmware_image) == 0) { while(1){ ;//Loop forever since we cannot boot } } else { do_boot(&firmware_image);}
It has a few problems, one of which is that, for example, using fault injection I can jump out of the while(1) loop and into the next branch, which has a call to boot_image(). This was an example of manipulating the control flow within a function, which stack canaries won’t catch.
The RCP also allows us to add a check within the code flow, to validate that the expected control flow occurred, as shown in Listing 3. This works by initializing a special counter that is incremented throughout the code. At the same time it’s incremented, the hardware validates whether the number on the RCP counter matches the expected number from the source code. So, in my example, if both code flows are executed, it would mean there would be one more count than expected, causing a mismatch (since the correct code flow executes one or the other branches of the if() statement, and my attacker code-flow executed both).
Listing 3
Validating the code flow.
rcp_count_set(5);rcp_count_check(5);if (check_signature(&firmware_image) == 0) { rcp_count_check(6); while(1){ ;//Loop forever since we cannot boot } } else { rcp_count_check(6); do_boot(&firmware_image);}
The RCP also helps with the problem of checking and storing values. Say, for example, that my simple Boolean response from the check_signature() call in Listing 2 has the problem that if the value is corrupted to any non-zero value, the wrong path is taken. Instead, these Booleans can be encoded as more complex values that are checked by the RCP, such that if the value is not one of two valid magic values, it again causes an exception. Additionally, the RCP can store integers as special values that are redundantly stored, and it can even perform various tests on different values.
The final key feature of the RCP is that instructions add random delays into the execution path. This makes performing fault injection much more difficult, since every execution will have slightly different timing. Typically, I’m exploiting highly consistent boot timings, when looking at attacks on devices.
FAULT INJECTION & SIDE CHANNEL TESTING
While the RCP sounds good in theory, how do we go about testing it? To help with testing this chip, we designed a special target board shown in Figure 1. This board includes a lot of features for testing both side-channel power analysis and glitch protection on this device. The design is available from an open-source repository with other ChipWhisperer targets [3]. One additional protection against glitches is a glitch detector. Back in my May 2020 article in Circuit Cellar, “Embedded System Essentials. Broad Market Secure MCUs: Spotlight on the MAX32520” [4], I ran some tests on a glitch detector on another device—which had limited success detecting the glitch. By comparison, the RP2350 glitch detector can be easily triggered; this is a good thing, meaning it is successful in detecting the glitches. But you can explore the glitch detector easily to understand how it’s used with this platform.

This target board includes features to test for side-channel power analysis and glitch protection.
As you can expect, it’s hard to get a design perfect the first time, and a few exploits were indeed found on the RP2350, as a result of a hacking challenge issued by Raspberry Pi [5].
SECURITY THROUGH TRANSPARENCY
There is a common phrase, “security through obscurity,” meaning that we try to avoid letting a would-be attacker know things about our device. This is usually the norm in the competitive microcontroller market, where you’d have to sign an NDA to learn about even the existence of attacks against the device.
In contrast, Raspberry Pi ran a hacking competition [5] that resulted in several attacks against it. I’ll briefly summarize them here, but check out the full blog post for details—including links to code and demonstrations [6].
The first attack by Aedan Cullen took advantage of the dual-boot mode of the RP2350. Using a voltage glitch, it was possibly to bypass the expected boot mode, and boot into RISC-V mode with debug enabled. This is related to how the OTP memory worked; it was possible to glitch this read to cause the device to come up in an unexpected mode.
The second attack by Marius Muench is related to the USB bootloader. While the fault injection mitigations I mentioned earlier are part of the bootloader, there was also a path that uses unsanitized input data which, in combination with a voltage fault injection attack, results in the device booting code that an attacker previously loaded into RAM. Luckily, there is a OTP setting to disable the feature required for this attack, so it’s less critical than the one by Cullen.
A low-cost laser fault injection setup was used by Kévin Courdesses to cause the hash function to calculate the hash over the wrong data. This means an attacker can cause the signature check to pass, because the firmware being checked isn’t the one that is actually about to be run! This is interesting, because it bypasses the glitch detectors using a laser fault injection platform, and it’s incredibly impressive to see such attacks working in practice!
At the higher end, IOActive used a much more advanced technique. A focused ion beam (FIB) was used to read out secret memory. Although it may seem like a high-budget technique, it might make sense in many situations. Recovery of bitcoin wallets is one that comes to mind, for example, but there are lots of high-value products where this could be in-scope.
Finally, Thomas Roth reported several interesting results, including a double-glitch attack that bypasses the OTP protection, allowing further reads and writes to an OTP page, when it should not be possible. Other evaluation results are likely to be released in the future, so keep an eye out for what has been done with the RP2350 device.
I hope you enjoyed this look at what I think will be the future of secure IoT microcontrollers—I think it will be the future not because of the specific piece of silicon, but the future due to the transparency built around the product.
REFERENCES
[1] RP2350 Boot Rom Source Code: https://github.com/raspberrypi/pico-bootrom-rp2350
[2] Colin Flynn, “Embedded System Essentials: Recreating Code Protection Bypass—An LPC MCU Attack.” Circuit Cellar #338, September, 2018.
[3] RP2350 Target Board Design Files: https://github.com/newaetech/chipwhisperer-target-cw308t/tree/main/CW312T_RP2350
[4] Colin O’Flynn. “Embedded System Essentials. Broad Market Secure MCUs: Spotlight on the MAX32520.” Circuit Cellar #358, May, 2020.
[5] RP2350 Hacking Challenge: https://github.com/raspberrypi/rp2350_hacking_challenge
[6] RP2350 Challenge Results: https://www.raspberrypi.com/news/security-through-transparency-rp2350-hacking-challenge-results-are-in/
PUBLISHED IN CIRCUIT CELLAR MAGAZINE • MARCH 2025 #416 – Get a PDF of the issue
Sponsor this ArticleColin O’Flynn has been building and breaking electronic devices for many years. He is an assistant professor at Dalhousie University, and also CTO of NewAE Technology both based in Halifax, NS, Canada. Some of his work is posted on his website (see link above).

