Skip to content

fix: feed full SE digest to PRNG reseed (drop 32-bit truncation) - #691

Draft
ballance wants to merge 1 commit into
Coldcard:masterfrom
ballance:fix/se-reseed-32bit-truncation
Draft

fix: feed full SE digest to PRNG reseed (drop 32-bit truncation)#691
ballance wants to merge 1 commit into
Coldcard:masterfrom
ballance:fix/se-reseed-32bit-truncation

Conversation

@ballance

@ballance ballance commented Aug 1, 2026

Copy link
Copy Markdown

Summary

mk4.rng_seeding() hashes the secure-element entropy with SHA256d but then keeps
only the first four bytes — n, = ustruct.unpack('I', n[0:4]) — before handing it
to ngu.random.reseed(). That throttled the secure-element contribution to the
software RNG to 2**32, discarding 28 of the 32 digest bytes.

This change passes the full 32-byte digest to reseed().

Change

  • shared/mk4.py: drop the 4-byte truncation; feed the whole SHA256d digest.
  • Re-pin external/libngu to the companion RNG fix.

Dependency / CI note

Draft — depends on switck/libngu#61; will re-pin on merge. #61 replaces the
generator with a SHA-256 Hash-DRBG and makes reseed() require a ≥32-byte seed
(it now rejects the old 4-byte int call), so this mk4.py change is required for
#61 to boot on-device. Until #61 merges, the pinned submodule commit lives only on
the fork and firmware CI cannot fetch it from upstream; the pin will move to the
merged #61 SHA.

Context

Public disclosure: Block engineering, "Predictable RNG fallback and 32-bit reseed in
COLDCARD firmware." This addresses the 32-bit-reseed half. (The predictable-fallback
half is already mitigated in this tree: stm32/COLDCARD*/rng.c binds rng_get() to
the hardware TRNG with #if MICROPY_HW_ENABLE_RNG -> #error guarding, and the
mpconfigboard.mk files poison pyb_rng_yasmarang.)

rng_seeding() hashed the secure-element entropy with SHA256d but then
kept only n[0:4], handing ngu.random.reseed() a single 32-bit word. That
throttled the secure-element contribution to 2**32 and, combined with
reseed()'s old behaviour, left most of the generator state at fixed
constants. Pass the whole 32-byte digest instead.

Bumps external/libngu to a512174, which reworks random.reseed() to
absorb a full-width seed into all generator state words.

Ref: Block engineering disclosure, "Predictable RNG fallback and 32-bit
reseed in COLDCARD firmware".

    Thirty-two silent bytes,
    all but four were cast aside —
    now the seed stays whole.

@scgbckbone scgbckbone left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please open libngu changes as separate PR against libngu repo? thanks

@ballance

ballance commented Aug 1, 2026

Copy link
Copy Markdown
Author

Can you please open libngu changes as separate PR against libngu repo? thanks

switck/libngu#60

@ballance
ballance marked this pull request as draft August 1, 2026 16:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants