When random.bytes() runs but doesn't work: The Coldcard firmware bug

I investigated the Coldcard hack and found a critical failure caused by a developer trying to fix a compiler error without understanding the code. By disabling the hardware RNG to silence a duplicate symbol error, the firmware silently switched to a weak random number generator. This incident highlights the dangers of shipping code you do not fully understand, especially when dealing with security-critical layers like C and Python bindings.
Micropython creates the illusion embedded developers do not need to understand C, their CPU, or other advanced concepts to do embedded programming. That is a lie.
- londons_explore
I am getting suspicious of "random number generator not actually returning random numbers" bugs....
They are the perfect bug for someone trying to "accidentally" make a secure system insecure.
I wonder if bribe money was involved or three letter agencies...
- anonymousiam
This post assumed that the author of the "runs" commit was not acting maliciously. Has that been established? It's not mentioned in this article.
- nullc
This writeup isn't very good and misses/misunderstands the programming error that leads to the flaw.
I'm commenting because I think it's important to understand the issue.
The article would have you think that the change in question was a tiny change to a flag to make it compile, but in reality the commit in question is a 1533 line addition of the entire RNG infrastructure.
The fundamental cause is a mixup between a value test and a definedness test.
Coldcard attempted to replace the micropython wrapper on the hardware TRNG, apparently in order to provide a more aggressive handling of fault/error conditions.
The micropython hwrng code is gated by an #if check, the replacement HWRNG code is gated by an ifndef. So "#define MICROPY_HW_ENABLE_RNG (0)" deactivated the micropython implementation but failed to activate the internal one (which was #ifndef MICROPY_HW_ENABLE_RNG ... which didn't fire because MICROPY_HW_ENABLE_RNG was _defined_).
This was easier to miss because the usages weren't only in different files-- they were in different repositories.
There is a more abstract point to make that in cryptographic software the absence of a secure randomness source (the STM32 TRNG) should never fall back to an insecure source (a trivial PRNG which might have only had on the order of 20-bits of uncertainty in its input). But the code that had the fallback was micropython which was not authored by the coldcard creators and is presumably not intended for cryptographic applications.. […]
- nonfamous
From the Twitter advisory on the issue being referenced here [1]:
>>> To every other developer: we believe this is a sober reality of the new AI paradigm. AI-assisted code review can now find latent bugs at a speed that is outpacing even the industry’s most seasoned experts. If your firmware is open-source or has ever been public, assume it's already being read by attackers and defenders alike.
Kinda turns the “many eyes” principle of OSS on its head, eh?
- koolba
> A good goal as a developer is a high commit message to change ratio. The more lines of code that you change, the more comments explaining why you’re changing the code. More message and less code changes per commit is generally a good idea.
No! Blanket statements like this is how you end up with 40 pages of slop AI comments in PRs that nobody reads.
Comments should be terse and meaningful. They should document surprising behavior or choices. The less comments you have, the more meaningful each one becomes because your time and eyeballs are limited as well.