FORENSIC CODE REVIEW · COLDCARD MK3 · FIRMWARE 4.x

No single line
made the seed weak.

A safe hardware path, an unsafe library assumption, a broken build guard, and an unchecked integration combined into one predictable wallet-seed path.

Authorship is evidence of control over a change. It is not evidence of intent.

01 / FAILURE CHAIN

Four correct-looking parts.
One unsafe result.

Each layer relied on an assumption from another layer. No layer verified the complete path from the seed request to physical entropy.

01Coldcard firmware

The safe path was replaced

New-wallet generation stopped calling Coldcard’s custom hardware RNG and started calling the libngu random interface.

ckcc.rng_bytes(seed) → random.bytes(32)View integration blame
02libngu

The adapter assumed the wrong symbol

For STM32 builds, libngu treated the global rng_get() function as the hardware random source. Coldcard’s custom hardware function used a different interface.

CHIP_TRNG_32() → rng_get()View adapter blame
03libngu guard

The build check tested existence, not value

Coldcard defined MICROPY_HW_ENABLE_RNG as 0. The #ifndef check saw that the name existed and allowed the unsafe build.

#ifndef MICROPY_HW_ENABLE_RNGCompare the board setting
04Linked binary

The call resolved to a software PRNG

Because the hardware implementation was disabled, the global rng_get() symbol came from MicroPython’s deterministic Yasmarang fallback, not the STM32 hardware RNG.

rng_get() → pyb_rng_yasmarang()View fallback source
RESULTnew wallet libngu global rng_get() software PRNGHARDWARE ENTROPY BYPASSED

02 / TIMELINE

The order matters.

Peter’s original 2018 configuration came first and was safe through its own interface. The harmful integration came after the libngu adapter existed.

  1. 01

    24 JUL 2018

    Peter D. Gray

    Coldcard uses its own RNG path

    The board disables MicroPython’s standard hardware-RNG feature and uses a custom Coldcard reader. This design works through ckcc.rng_bytes.

  2. 02

    28 JAN 2021

    Switck

    libngu adds its STM32 adapter

    The adapter calls global rng_get(). Its safety guard uses #ifndef and cannot detect a macro whose value is 0.

  3. 03

    01 MAR 2021

    Peter D. Gray

    Seed generation moves to libngu

    The integration replaces the verified Coldcard path without confirming which rng_get() implementation the final binary links.

  4. 04

    17 MAR 2021

    Coldcard v4.0.0

    The path reaches users

    The release turns two dormant compatibility problems into a security defect in new-wallet seed generation.

BEFORE 2021

Coldcard’s unusual configuration was not enough to cause the defect.

AFTER 01 MAR 2021

The new integration connected every failure condition.

03 / RESPONSIBILITY

Who had the decisive control?

This assessment ranks direct engineering responsibility for the defect. It does not claim malicious intent or assign legal liability.

MY ASSESSMENT

Most responsible for the user-impacting bug

Coldcard’s integration and release owner

The decisive change moved the most sensitive operation—new wallet-seed generation—across a library boundary. That change required proof that the final firmware still reached hardware entropy. The March 2021 integration did not preserve or verify that invariant.

COMMIT AUTHORPeter D. GrayInspect commit ↗
PRIMARY
Coldcard integration and releaseCommit author: Peter D. Gray

The integration changed the security boundary for wallet seeds. It made the unsafe path reachable and shipped it without a binary-level source check.

SIGNIFICANT
libngu STM32 adapterCommit author: Switck

The adapter made an unsafe platform assumption. Its incorrect preprocessor guard failed at the exact job it was meant to perform.

SYSTEMIC
Coldcard review and test processOrganizational responsibility

Review and release tests did not prove entropy provenance in the linked firmware. Output statistics could not detect this failure.

CONTEXT
MicroPython fallbackUpstream behavior

The fallback supplied the weak stream, but it followed the disabled-hardware configuration. The higher layers should have rejected or avoided it.

THE COUNTERFACTUAL TEST

Remove either 2021 change and the seed defect disappears.

Without Switck’s faulty adapter: the build stops or connects to the correct hardware source.

Without Peter’s integration: Coldcard seed generation stays on its existing hardware-RNG path.

04 / NASA–JPL COUNTERFACTUAL

Could the “Power of Ten” have saved it?

Yes—but not through one magic rule. Four JPL rules would have reduced the risk. Independent verification of the compiled release would have provided the strongest stop.

WHAT THESE RULES ARE

A compact JPL coding discipline for safety-critical C

Gerard Holzmann developed the Power of Ten at the NASA/JPL Laboratory for Reliable Software. They are coding guidelines, not proof that software is safe and not rules that governed Coldcard.

Read all ten rules
RULE 05

Assert critical invariants

Use assertions to detect conditions that must never occur.

APPLIED TO COLDCARD

Assert that the selected entropy source is a hardware RNG. A byte-diversity check is not enough because a software PRNG can produce varied-looking output.

A source-aware assertion stops startup or the build.

Rule text ↗
RULE 07

Validate every interface

Check function results and validate all parameters.

APPLIED TO COLDCARD

Make libngu receive an explicit hardware-RNG provider with a verifiable capability. Do not discover a security dependency through a global symbol name.

The incompatible Coldcard adapter cannot be connected.

Rule text ↗
RULE 08

Restrict the preprocessor

Limit preprocessor use to inclusion and simple macros.

APPLIED TO COLDCARD

Remove hidden platform selection from #ifdef branches. If a build condition is unavoidable, test the macro value, not only whether its name exists.

MICROPY_HW_ENABLE_RNG=0 fails closed.

Rule text ↗
RULE 10

Analyze every build

Enable all warnings and run strong static analyzers.

APPLIED TO COLDCARD

Analyze each board configuration and inspect the final link map. Add a release check that proves rng_get resolves to the intended hardware implementation.

The unexpected software symbol becomes release-blocking evidence.

Rule text ↗

THE CONTROL MOST LIKELY TO SAVE THE RELEASE

Trace the entropy requirement into the signed binary.

01Requirement

Wallet seeds must originate from the STM32 hardware RNG.

02Build evidence

The link map must identify the approved hardware implementation.

03Binary test

Disassembly and hardware tests must confirm the release path.

04Independent gate

A reviewer who did not write the integration must approve the evidence.

Why this works

Source review alone can miss symbol resolution. This gate checks the exact artifact sent to users. NASA’s active software-assurance standard calls for a systematic life-cycle approach that includes independent verification and validation.

NASA-STD-8739.8B ↗

Important limit: Rules 8 and 10 would improve detection, but they do not guarantee it. A static analyzer may accept the legal C code. The release-level entropy trace is the decisive safeguard.NASA Software Engineering Handbook ↗