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
#ifndefguard inlibngu/ngu/random.c, still testing defined-ness rather than value random_reseed(), still truncating to a single 32-bit wordMICROPY_HW_ENABLE_RNG, still(0), withgenerate_seed()still callingngu.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.
#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.
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.