Companion to the Coldcard RNG vulnerability analysis.

Why this post exists

The RNG analysis showed that seeds generated on the device via “New Wallet → Generate Words” are weak — brute-forceable. The advice there was blunt: treat on-device-generated seeds as compromised and sweep them to a fresh seed.

That raises an obvious question if you’re self-custodying Bitcoin behind vulnerable seeds and you don’t have an alternate hardware wallet on hand: can you trust the same Coldcard to make a safe seed with dice, or do you need to buy new hardware first?

This is the report I ran to answer that. The short version: dice works. You don’t need a second device to get a sound seed — you need dice and honest rolls (50 for 12 words, 99 for 24 — and you should probably just roll all 99).

Can I use the same Coldcard to generate a new key?

Yes. A Coldcard holds one master seed in its secure element at a time. To replace it with a fresh dice seed, the old one has to be destroyed first — the device must be empty. That is destructive: the old words are erased forever.

Before you destroy anything: make sure your original seed words are backed up and verified. The Coldcard does not let you view your seed words again after creation — you either wrote them down correctly when the seed was made (the word quiz after generation tests this) or you didn’t. If you can’t restore the old wallet from your written words, destroying the master seed means losing access to those funds forever. Verify your backup now, before you destroy, generate the new dice seed, and sweep funds from the old addresses to the new ones.

There’s also a non-destructive option: temporary seeds — held in RAM, lost on reboot unless saved to the Seed Vault (v5.2.0+, Mk4+), which can store multiple seeds in encrypted settings. A temporary seed lets you generate a dice seed, sign with it, and switch back to the master without destroying anything — useful for a multisig key or a balance check.

Either way, you don’t need new hardware to get a safe dice-rolled seed on the same Coldcard.

Is the dice path actually safe from the RNG bug?

Dice-generated seeds are SHA256 over the literal ASCII string of your rolls — nothing else. No device RNG, no boot state, no constants. The running hash is shown on screen after every roll, so you can cross-check it against sha256sum on an airgapped machine. The dice core is byte-identical in Mk5 v5.5.0 and Q v1.3.3Q.

The broken RNG doesn’t reach this path. Dice is the escape hatch the RNG broke.

How it works

add_dice_rolls() (Q: line 334):

md = sha256(seed)            # seed = b''  →  sha256 of empty string
while 1:
    ch = await pr.wait()     # keypad
    if ch in '123456':       # only digit keys; everything else ignored
        count += 1
        md.update(ch)        # one ASCII byte per roll ('1'..'6' = 0x31..0x36)
    ...
if count:
    seed = md.digest()       # final seed = SHA256('31562...')  — the rolls, verbatim

I read the whole function and grepped its callees. There is no ngu.random, no ckcc.rng, no callgate, no constant material anywhere in it. The vulnerable RNG chain from the other post is never touched — the 32-bit secure-element reseed at boot is irrelevant here.

The on-screen hash is a receipt

Both UIs display roll count plus the full 64-hex running digest (ux_mk4.py:494-501, ux_q1.py:786-793). The first screen shows e3b0c4…b855 — that’s SHA256(''), the hash of the empty string, matching Coldcard’s verification doc.

Reproduced by hand:

  • sha256(b'') = e3b0c442…b855
  • sha256(b'123456') = 8d969eef…6c92

Any roll sequence checks out with echo -n <rolls> | sha256sum.

After the hash

md.digest() (32 bytes)
  → approve_word_list(): 12w → seed[0:16]   (128-bit truncation, correct BIP-39 length)
  → bip39.b2a_words()                       (entropy + checksum bits → 11-bit words)
  → word quiz → commit_new_words()
  → stored in secure element

Every stage is a pure function of the digest. Nothing in this chain can swap in an RNG value — and because the hash evolves per roll in lockstep with sha256sum, any substitution would be visible to you on screen. The BIP-39 encoding is standard (libngu/ngu/bip39.py:318).

The math

  • Per D6 roll: log₂6 = 2.585 bits
  • 50 rolls → 129.25 bits ≥ 128-bit floor for 12 words ✓
  • 99 rolls → 255.91 bits — 0.09 bits shy of 256 (Coldcard’s standing convention; negligible)
  • No modulo reduction anywhere → no modulo bias. 24 words consume the full 32-byte digest.

Which dice paths enforce enough rolls (and which don’t)

The Coldcard has four places where dice rolls can enter seed generation. Three of them force you to roll enough; one lets you click through a warning; one has no minimum at all:

Menu path Minimum rolls Can you do fewer?
New Seed Words → Advanced → Dice Roll (12w / 24w) 50 / 99 No — hard block, you can only add more or abort
Paper Wallet → Use Dice 99 No — hard block, plus the result must be valid (< secp256k1 order, non-zero)
Temporary Seed → Dice Roll 50 / 99 Yes — warning shown, but you can confirm past it
Mix-in (press 4 while reviewing an RNG seed) none Yes — no minimum, no warning, no quality check

The first two are the paths you’d use to make a real wallet. They won’t let you cheat yourself. The temporary-seed path is softer — it warns but doesn’t stop you. The mix-in path is a different beast — read on.

Mix-in mode: adds rolls to an RNG seed

Pressing (4) while reviewing an RNG-generated seed computes SHA256(rng_seed ‖ rolls). This produces a new seed — not a tweak to the old one. New seed means new BIP-39 words, a new master key, and a new set of addresses. The old wallet’s addresses are abandoned either way; you still have to sweep funds to the new ones.

What mix-in gives you is a seed whose entropy is both the (weak) RNG output and your rolls. Even with the broken RNG (~32-bit pad), an attacker still needs your roll sequence — dice entropy is additive under SHA-256. The catch is that you’re building on a compromised base, so you need enough rolls to drown out the 32-bit weakness: ≥38 honest rolls to reach 128-bit effective security. The firmware won’t tell you that number (see the gaps below).

Pure dice is cleaner: the RNG never touches it, the whole thing is hand-verifiable, and there’s no compromised base to overcome. Mix-in is the middle ground when you want the RNG output factored in — but it is not a way to “keep your old wallet.”

Two gaps worth knowing

Neither is a code flaw in the dice path. Both are about where enforcement stops.

1. Temporary-seed dice is soft-enforced

ephemeral_seed_generate_from_dice() calls the dice function with enforce=False. Roll 10 dice (~26 bits), press ENTER, and the low-entropy warning is a yes/no confirm — one more keypress and the seed is built. Same for the loaded-dice (>30% one face) warning.

The permanent path makes both checks non-negotiable. The temporary-seed path treats them as advice. Temporary seeds are for quick/temporary wallets — exactly where someone skims rolls and clicks through. The resulting wallet looks like any other; nothing flags it as a 26-bit seed.

2. Mix-in has no gates at all

When you press (4) on an RNG seed, it calls add_dice_rolls(0, seed, False) (seed.py:676) — judge_them=False. That flag drops every quality check:

  • No count check. One roll, ENTER, done — 2.6 bits added, no warning.
  • No distribution check. Pressing 1 fifty times mixes in a deterministic suffix, silently accepted.
  • No target feedback. Nothing tells you that to lift a compromised ~32-bit RNG seed to 128-bit you need ≥38 honest rolls ((128−32)/2.585). The UI never says.

Defensible in a vacuum — the base seed exists, dice can only add, so no floor is logically required. Dangerous given the RNG vuln: a user who learns the RNG is broken, mixes in 10 rolls, and feels safe has ~2³² × 2²⁶ ≈ 2⁵⁸ of work against them — brute-forceable, and the firmware gave no signal that 10 wasn’t enough.

One more thing: mix-in is not hand-verifiable. The starting hash is SHA256(rng_seed), whose input you don’t know — so the sha256sum cross-check only works for pure dice.

A note on multisig

If you’re holding enough that a single-key compromise is catastrophic, you’re probably already in or considering multisig — multiple keys, multiple devices, so no single seed failure drains the wallet. The standard custody advice thresholds multisig somewhere around meaningful five-figure holdings and up.

The RNG vuln sharpens why that matters: in a multisig, a Coldcard-generated seed is a privacy leak if it’s one of several keys, but funds are only at risk if a majority of your co-signers used Coldcard-generated seeds. Dice-generated seeds on a Coldcard are fine as a multisig key — this post is the verification of that. The device contributes nothing to the seed and nothing leaks.

If you’re migrating off a compromised single-key setup and multisig is on the table, dice-rolling a fresh Coldcard key (or several) is a legitimate way to build it without buying new hardware today.

Things the firmware can’t do for you

  • It can’t detect dishonest rolls. The only quality gate is “any face >30% → reject/warn.” An evenly spread but predictable sequence (123456123456…) passes. Entropy comes from the physical dice, not the device.
  • Abort early. Cancellation only works before 10 rolls; after that, entered rolls are discarded wholesale on cancel (never partially used — correct, but plan the session).
  • Verify offline. Checking rolls on a networked machine compromises them. Use an airgapped or Tails system per the official doc.

Sweeping your funds to the new seed

Once you have a fresh dice-rolled seed, you move your Bitcoin by spending every UTXO from the old wallet to an address on the new one — a single transaction (or a few) that empties the old wallet entirely. This is a sweep: it doesn’t “move” coins, it spends them into new outputs the old seed can’t reach.

The on-chain link is permanent. Anyone watching the blockchain sees the full balance travel from the old addresses to the new ones. That’s fine for security — the old seed is compromised and you’re abandoning it — but it means the new wallet inherits the old one’s transaction history on every block explorer. If privacy matters to you, read on.

This is also a consolidation opportunity. If your old wallet accumulated many small UTXOs (mining payouts, repeated deposits, Lightning channel closes), the sweep can fold them all into a single output on the new seed. Consolidation is cheapest at low fee rates — 1 to 5 sat/vB, typically weekend evenings UTC — and saves you from paying for dozens of inputs later. The trade-off: consolidation publicly links every UTXO you combine, so it’s a fee play, not a privacy play. (UTXO management guide)

CoinJoin after the sweep breaks the trail. Once funds are on the new seed, a CoinJoin mixes your UTXO with others’ so the common-input heuristic can’t tie the new wallet back to the old one. The order matters: sweep first (abandon the compromised seed), then CoinJoin on the new wallet’s UTXO — never mix from the compromised seed, and never re-consolidate mixed outputs together afterward. The CoinJoin tooling landscape changes frequently (some coordinators have been shut down), so check what’s currently operational before relying on one — this comparison of Bitcoin privacy tools covers CoinJoin, PayJoin, and other techniques that are available now.

Active exploitation is underway — 594 BTC was drained in 25 minutes from this same flaw. But urgency isn’t a reason to rush into a preventable mistake. Confirm your plan with someone who knows this stuff before you destroy a seed or move funds. Back up the new words, verify them, test the restore — then sweep. You’re balancing speed against the risk of losing funds to a botched migration, and the latter is permanent too.

Bottom line

Dice seeds on both Mk5 v5.5.0 and Q v1.3.3Q derive solely from your rolls via a hand-verifiable SHA-256 construction, encoded to BIP-39 by the book. The RNG vulnerability doesn’t reach this path. ≥99 honest D6 rolls (24 words) or ≥50 (12 words) are as strong as your dice — the device contributes nothing, for better and for worse.

If you generated a seed with the broken on-device RNG and you don’t have an alternative wallet, this report is here to help you understand the risk profile of using the same Coldcard to regenerate with dice rolls — instead of panicking and sending your funds to an exchange. Self-custody is the whole point of a hardware wallet, and it’s incredibly important to the Bitcoin ecosystem. You don’t have to give that up to be safe: a fresh pure-dice seed on the same device gives you new words, new addresses, and a wallet as strong as your dice. Mix-in is possible, but you’ve already learned the hard way that low entropy is bad for your mojo — do the needful and roll all 99.

Sources