COLDCARD vulnerability what happened, and what to do
Informational only, and this site never asks for your recovery words. details

Informational only. This is independent analysis and an evidence-backed explainer, not financial, security or legal advice, and not a substitute for professional advice about your own situation. It is not affiliated with, endorsed by, or speaking for Coinkite, Block, or any other party named here. Published estimates are attributed, and differing scenarios are kept separate with their assumptions. Act on your own judgement. Editorial standards and corrections.

Do not disclose recovery material to a website, form, message or support account. This site never asks for it. Deliberate recovery on independently verified offline equipment is a separate operation. Seed-word safety.

Technical Updated 1 Aug 2026

How the fix works

The fixed build supplies a hardware-backed rng_get implementation, excludes the MicroPython fallback source and checks the linked symbols after compilation.

Firmware and cryptographic detail.

The hotfix fixes the COLDCARD integration at the link and build layer. An immutable mainline commit patch records the normal Mk4/Mk5/Q source change. The merged Mk3 pull request #689 and Edge pull request #690 preserve the separate branch records. The hotfix does not change libngu's upstream guard or reseed implementation. The relevant source and pinned submodule revisions for these three items are unchanged as of 1 August 2026:

  • the #ifndef guard in libngu/ngu/random.c, still testing defined-ness rather than value
  • random_reseed(), still truncating to a single 32-bit word
  • MICROPY_HW_ENABLE_RNG, still (0), with generate_seed() still calling ngu.random.bytes(32)

What changed is the link. COLDCARD's own board rng.c now exports rng_get() itself, backed by the real peripheral:

stm32/COLDCARD_MK4/rng.c:82-85, new in the hotfix

uint32_t rng_get(void)
{
    return rng_get_or_fault();   // real STM32 peripheral, OSError on timeout
}

The fixed source configuration excludes MicroPython's rng.c, the file containing the Yasmarang fallback, by compiling an empty object in its place:

stm32/COLDCARD_MK4/mpconfigboard.mk:96-101, fixed release source

# Do not compile MicroPython's fallback PRNG.  The board-specific rng.c
# provides rng_get(), and this empty object satisfies the upstream object list.
$(BUILD)/rng.o: CFLAGS += -Dpyb_rng_yasmarang=error-do-not-want-this
$(BUILD)/rng.o: $(BOARD_DIR)/mpconfigboard.mk
	$(ECHO) "SKIP stm32/rng.c"
	$(Q)$(CC) $(CFLAGS) -x c -c /dev/null -o $@

The fixed build also runs rng-code-check after compilation. It uses arm-none-eabi-nm to require that the replacement upstream rng.o defines no symbols and that the board object defines a global text symbol named rng_get. Either mismatch stops the build.

Properties of the integration fix Changing #ifndef to a value check would make the unsupported configuration fail at compile time. The COLDCARD hotfix instead supplies the board's hardware-backed rng_get() symbol and configures the MicroPython fallback source not to compile for this object target. The conspicuous replacement name would make accidental reintroduction visible. This project verified the source configuration, not the contents of a shipped binary image.
flowchart TB
  subgraph AFTER["AFTER THE HOTFIX"]
    direction TB
    CALL["generate_seed
ngu.random.bytes 32
UNCHANGED"] --> MRB["my_random_bytes
UNCHANGED"] MRB -->|"CHIP_TRNG_32
still resolves to rng_get"| NOW["board rng.c rng_get
NEW"] NOW --> HWOK["STM32 RNG peripheral
raises OSError on timeout"] MRB --> NGU2["libngu Yasmarang
UNCHANGED, still XORed in"] HWOK --> XOR3(("XOR")) NGU2 --> XOR3 XOR3 --> GOOD["32 output bytes
incorporating STM32 RNG words"] end GONE2["MicroPython Yasmarang source
excluded by fixed build rule"] classDef good fill:#1c7c4a22,stroke:#1c7c4a,stroke-width:2px classDef same fill:#2160b822,stroke:#2160b8,stroke-width:1.5px classDef gone fill:#88888822,stroke:#888888,stroke-width:2px,stroke-dasharray:6 4 class NOW,HWOK,GOOD good class CALL,MRB,NGU2 same class GONE2 gone

The relevant application and libngu source is unchanged. The fixed build resolves rng_get() to the board implementation and excludes the MicroPython fallback source. Binary contents have not been independently checked here.

Scope of the remaining upstream issue libngu's #ifndef guard is unchanged in the public repository. Another STM32 integration is exposed to the same failure mode only if it defines MICROPY_HW_ENABLE_RNG as zero, resolves rng_get() to the MicroPython fallback and relies on that output for a security-sensitive operation. Pull request #58 proposes a value check; it remains unmerged as of 1 August 2026.

Evidence and calculations are scoped beside the claims they support. Device-state attack-cost scenarios are compared in what an attack costs, with each source's assumptions written out. Where sources disagree, their scenarios are kept separate. If something here is wrong, say so.