HN Debrief

When random.bytes() runs but doesn't work

  • Security
  • Cryptocurrency
  • Embedded Systems
  • Programming

The post looks at a firmware flaw in Coldcard hardware wallets where code intended to supply secure random bytes could run without ever reaching the STM32 hardware true random number generator. In plain terms, wallet seed generation could silently fall back to much weaker pseudo-randomness, which is catastrophic in a device whose whole job is protecting private keys. The article framed this as a lesson in sloppy engineering around a huge commit with the message "runs", weak review culture, and overconfidence in embedded code that mixes MicroPython and C.

If you ship security-critical code, treat entropy paths as a first-class attack surface and test them by breaking dependencies on purpose, not by eyeballing output. Also audit the compiled binary, not just source, because a dead hardware RNG path can vanish cleanly while the product still appears to work.

Discussion mood

Alarmed and harsh. Most comments treated this as an unforgivable failure in a security-critical product, while also knocking the article for over-indexing on commit-message hygiene instead of explaining the real preprocessor bug and the testing blind spots around entropy.

Key insights

  1. 01

    The bug was a macro mismatch

    The failure came from mixing up a value check with a definedness check across two codebases. Setting `MICROPY_HW_ENABLE_RNG` to `0` disabled one hardware RNG path, but a separate `#ifndef` gate never opened the replacement path because the macro was still defined. That matters because it turns the incident from "someone ignored compiler noise" into a deeper integration error that source review could easily miss, while the compiled binary might make the absence of any hardware RNG access obvious.

    Add lint rules that forbid mixing `#if FOO` and `#ifdef FOO` conventions for the same config flag. For security-critical firmware, inspect binaries or generated call graphs to confirm the entropy source is actually referenced in shipped builds.

  2. 02

    Randomness tests can validate the wrong thing

    A weak pseudo-random generator can still pass the kind of output checks teams commonly run, so extensive randomness testing does not prove the hardware entropy path is live. Extra fallbacks and whitening can hide the absence of true entropy even better. The practical failure mode is brutal. You think you tested the RNG, but you only tested that some deterministic generator emits bits that look noisy.

    Test entropy pipelines with fault injection. Disable or stub the hardware RNG and verify seed generation fails hard instead of producing plausible-looking output.

  3. 03

    This class of preprocessor bug is preventable

    The defined-versus-value confusion is not exotic. It is the kind of mistake that stricter build rules can flush out early. Using `-Wundef` and forcing a single convention for config macros makes it much harder for `0`, undefined, and `#ifdef` semantics to drift apart across files and repositories.

    Standardize config macros as always-defined or always-undefined and enforce that choice in CI. Treat preprocessor warnings as release blockers in firmware and crypto code.

      Attribution:
    • RustyRussell #1
    • nullc #1
  4. 04

    AI changes review economics, not the root duty

    Several comments accepted the core claim that AI-assisted review makes latent bug hunting cheaper and faster, for attackers and defenders alike. But the useful framing was narrower than the article's rhetoric. LLMs help surface low-hanging security flaws, yet the real embarrassment is failing to run regular security review at all. Open versus closed source does not save you much when binaries are available and sometimes easier to inspect than misleading source paths.

    Assume your released firmware is being machine-audited right now. Use the same tooling internally, but do not let "we use AI review" substitute for scheduled human security review and release checks.

      Attribution:
    • dist-epoch #1
    • abecedarius #1
    • catlifeonmars #1
  5. 05

    Tool choice is only a weak proxy

    Using MicroPython on a hardware wallet triggered strong reactions, but one commenter made the sharper point that teams often mistake style markers for actual engineering rigor. "Wrong" tools can still be used carefully, and "right" tools can still ship broken designs. In this case the bad outcome came from weak understanding, poor integration, and missing safeguards, not from a single language choice alone.

    Do not let architecture reviews stop at language wars. Ask how entropy failures are surfaced, how boundaries are tested, and which assumptions are mechanically verified.

      Attribution:
    • nullc #1

Against the grain

  1. 01

    The product model is the deeper flaw

    A minority view rejected the idea that this is mainly an implementation cautionary tale. The stronger criticism was that self-custodied crypto asks ordinary users to bear catastrophic software risk with little recourse when things go wrong. Banking systems also fail, but they usually have remediation paths. That changes the acceptable error budget in a way wallet culture often handwaves away.

    If you build or buy systems that put loss recovery entirely on the user, set a much higher bar for operational safety and user education. If that bar is unrealistic for your audience, the product model may be wrong for them.

      Attribution:
    • hypeatei #1 #2
    • anonymars #1
  2. 02

    Commit message ratios are a bad metric

    The post's "message length divided by lines changed" heuristic got pushback for rewarding verbosity and even AI slop. Better commit messages are about intent, constraints, and non-obvious decisions, not character count. One comment also noted the article compared a commit message from one project with what was actually a richer pull request description from another, which weakens the benchmark entirely.

    Review for informational content, not prose volume. In your process, require commit or PR text to explain why the change exists, what assumptions it relies on, and how it was validated.

      Attribution:
    • koolba #1
    • coldbrewed #1
    • PunchyHamster #1

In plain english

`#ifndef`
A C preprocessor directive that includes code only when a macro is not defined at all, regardless of its value.
C preprocessor
The part of the C build system that handles macros and conditional compilation before the compiler processes the code.
MicroPython
A small implementation of the Python programming language designed to run on microcontrollers and other constrained devices.
RNG
Random number generator, software or hardware that produces numbers intended to be unpredictable.
STM32
A widely used family of 32-bit microcontrollers from STMicroelectronics, common in embedded systems.
whitening
A process that transforms raw random input to reduce visible bias or structure in the output.

Reference links

Primary references and code

Background on randomness testing

Advisories and social posts

Related discussion on manual entropy