Coldcard postmortem from MicroPython's perspective 路 micropython 路 Discussion #19588 路 GitHub
//discussions/show" data-turbo-transient="true" />
Skip to content
Type / to search
Sign in<br>Sign upAppearance settings
You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.
Dismiss alert
{{ message }}
MicroPython
Coldcard postmortem from MicroPython's perspective
#19588
dpgeorge
announced in<br>Announcements and news
Coldcard postmortem from MicroPython's perspective
#19588
dpgeorge
Aug 7, 2026<br>·<br>1 comment
Return to top
Discussion options
Uh oh!
There was an error while loading. Please reload this page.
{{title}}
Something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Quote reply
dpgeorge
Aug 7, 2026
Maintainer
Coldcard is a Bitcoin hardware wallet, and it's firmware is built using MicroPython. (Trezor is another hardware wallet also using MicroPython, but it's not relevant here and not discussed further.)
Recently a vulnerability in the Coldcard firmware was exploited to steal over $100M worth of Bitcoin. The vulnerability existed due to the accidental use of a pseudo random number generator (PRNG) instead of a true (hardware) random number generator.
Here's my analysis of how this vulnerability came to exist, from the perspective of the use of MicroPython in the Coldcard firmware.
Analysis
The stm32 port provides a rng_get() function, which can either be hardware backed or a PRNG (which is seeded by the RTC and unique id). You select that based on MICROPY_HW_ENABLE_RNG (set to 1 to get HW RNG, set to 0 for PRNG).
Coldcard firmware has a submodule called libngu. This library externally references (and expects to link against) rng_get(). The relevant code is https://github.com/switck/libngu/blob/0371d6372eb7c1165f9c0410f6d6537e09882402/ngu/random.c :
#ifdef MICROPY_PY_STM<br>// ports/stm32/rng.c<br>extern uint32_t rng_get(void);<br># define CHIP_TRNG_SETUP()<br># define CHIP_TRNG_32() rng_get()
# ifndef MICROPY_HW_ENABLE_RNG<br># error "get a HW TRNG plz"<br># endif<br>#endif
There is a macro check there which is trying to enforce the HW RNG, but it's only checking that MICROPY_HW_ENABLE_RNG is defined, rather than checking it's enabled.
The Coldcard firmware has a custom rng.c implementation in the board folder, which is included correctly in the firmware build. See https://github.com/Coldcard/firmware/blob/bcc2c382a324690a2fcf972c0bac3b79bf923f7b/stm32/COLDCARD_MK4/rng.c
That rng.c file implements rng_get_or_fault() which uses the stm32 hardware RNG source (raises a Python exception if the hardware fails).
The Coldcard MicroPython firmware builds and links fine with no duplicate symbols because they define rng_get_or_fault() which does not clash with the existing stm32-provided rng_get().
The Coldcard MicroPython firmware builds and links fine with no missing symbols because the rng_get() that libngu expects to call is provided by the MicroPython stm32 source code.
Now, libngu doesn't know about rng_get_or_fault() but it should be calling it instead of the stm32 code.
At this point you can see the vulnerability: libngu is using the PRNG implementation in stm32 because MICROPY_HW_ENABLE_RNG is set to 0. The PRNG algorithm can be inspected to create an exploit.
Note this is all at the C level, there's no Python code involved here (not even Python-C bindings). It's a confluence of C-level (mis-)naming, build systems, submodules and configuration variables.
Fixes made post exploit
Post exploit, the fix to the Coldcard firmware was made: Coldcard/firmware@ca72463
That fix "deletes" the stm32's rng.c code and adds a wrapper uint32_t rng_get(void) { return rng_get_or_fault(); } function so that libngu gets the hardware RNG (alternatively, could have changed libngu's CHIP_TRNG_32 macro to call rng_get_or_fault() instead).
There was also a fix to libngu to fix the macro check discussed above. See switck/libngu@e9d5e80
Lessons for MicroPython
MicroPython is used in many places, many which are open source and many which are kept closed. Due to the nature of embedded systems, those uses of MicroPython are sometimes in critical systems.
The engineers of the critical systems are responsible for ensuring their systems function correctly and are secure.
That said, it's also good practice for MicroPython to facilitate the construction of robust and secure systems, at least where practical and reasonable. When writing code related to random numbers, encryption, cryptography and other security-related components, it would be beneficial for MicroPython to think widely about how such code might be used, and structure the code to eliminate any obvious mistakes in its use.
You must be logged in to vote
馃憤
All reactions
馃憤
Replies:
1 comment
Comment options
Uh oh!
There was an error while...