Reference · Bitcoin seed generation
How to roll a seed you can verify: what actually costs you entropy, what is not worth worrying about, and what each wallet does with the rolls once you hand them over.
Use the dice you have, roll the count your wallet asks for, and you are done.
Every Bitcoin wallet has to produce a seed from somewhere and almost all of them use the device's own random number generator, its RNG. You are then trusting that RNG and there is nothing in its output you can inspect to tell whether it did its job (see: the 2026 Coldcard incident, §20).
Dice are one answer for when you would rather not take that on trust. The rolls are the only input, so the seed is a pure function of something you watched happen, reproducible elsewhere to confirm the device did what it should.
You have to trust a seed a device creates for you. You can verify a seed you rolled yourself.
These methods trade against each other rather than ranking cleanly. Three dimensions separate them: what you have to obtain before you start, what you have to do once you have it, and how much of the result you can check afterward.
| Method | Setup | Procedure | What to verify | |
|---|---|---|---|---|
| On device | Device RNGwhat most wallets do | None | Automatic, instant | Input can't be verified |
| Camera imageSeedSigner's camera | None | Aim, review, accept | No external verification | |
| Dice, hasheddevice calcs words | Grab some dice | 99 rolls, typed in | Full mnemonic | |
| By hand | Coin flipsbinary to words | Coins + worksheet | Lookups or binary math | Just the final word |
| Dice worksheetrolls to words | Dice + worksheet | Rolls, rerolls, lookups | Just the final word | |
| Word pickingdraw from 2048 slips | Cut out 2048 slips | Draw, then type in | Just the final word |
no cost minor cost a real cost the method's weak point
The vertical brackets on the left indicate where the work happens. Hashed dice sit in neither group: the rolls happen in the real world but the device plays an integral role.
10100101101, manually convert binary to decimal, or type all 256
flips into a device.No route here is machine-free. A BIP-39 phrase ends in a checksum word, so that word gets computed however you reached the other twenty-three (§12). The last column is what each method leaves you to verify.
Which column matters most is yours to decide, and reasonable people land in different rows. But one row never drops below easy.
Dice is a reasonable compromise that holds up well enough in all three columns. The dice are ones you already own, the procedure adds nothing to the rolling beyond typing the digits in, and the result reproduces anywhere from what you wrote down. Every other method on this chart offsets its strengths with a drawback somewhere: a costly setup, an awkward procedure, or a result nobody can check.
Not enough to change anything you do. A biased die does cost you bits, so the only question is how many.
Every figure below is a worst case rather than an estimate. A real die does better.
Not much has been published on this. These two studies are the most substantial we found.
| Measurement | Dice | Rolls | Worst case found |
|---|---|---|---|
| Labby, 2009 | 12 cheap plastic | 315,672 | one face 1.3% high |
| Iversen et al., 1971 | 58 cheap, 161 precision | 4,380,000 | cheap dice 1.4% high on even faces; precision dice, nothing |
Two studies, four decades apart, measuring different things, both landing near 1.4%. This page works from 2%, which deliberately exaggerates both. §17 has the links and the caveats.
■ 24-word seed, 99 rolls ■ 12-word seed, 50 rolls
A perfectly fair die
A cheap die, one to three faces 2% high
One face 8% high, four times the working figure
20% more often, visibly odd
50% more often, obviously defective
Any bias large enough to matter is large enough to notice casually. The one that has actually been measured took a machine and 315,672 rolls to pin down.
Casino dice do not matter here. They are provably better, but not in a way that makes any difference to the entropy you end up with. The measurement is in the appendix.
Extra rolls are not necessary. Advice to roll 120 or 150 times circulates on the theory that they buy margin against an imperfect die. They would, but §2 leaves no such die to guard against: a real one costs almost nothing, and one skewed far enough to matter would be so obviously bad you would not have used it in the first place.
There is a ceiling. A seed holds a fixed number of bits, 128 for a 12-word phrase and 256 for a 24-word one. 99 rolls already deliver 255.9 of that 256. Roll 150 times and the surplus has nowhere to go.
Spreading FUD. Someone who has convinced themselves that 120 rolls are necessary goes on to tell everyone who “only” rolled 99 that their seed is deficient.
Doubt spreads faster than the correction does and it lands on people who did nothing wrong.
A seed rolled to the count its wallet asks for is complete. Nobody who rolled that way has anything to fix.
The roll count is not a floor to exceed for safety. It is the number that fills the seed.
A die remembers where it started. Kapitaniak and colleagues found that the face which starts at the bottom is the one most likely to finish there, and that how much depends almost entirely on bouncing (§17).
| How the die lands | Chance it keeps its starting face |
|---|---|
| Dropped on a soft surface, no bounce | 54.8% |
| Thrown from a hand or cup, 4 to 5 bounces | 19.9% |
| A perfectly fair die | 16.7% |
Their conclusion is worth quoting: “It is not enough for a die which is fair by symmetry to be fair by dynamics.”
So make sure every roll gets a real tumble, several bounces before it settles, whether that comes from your hand or from a box. It is the one thing here that can genuinely weaken a seed, and the easiest to get right.
This is where casino practice stops being a useful guide. A craps table makes dice hit a studded back wall to stop a player from exploiting this determinism for money. You are not trying to stop yourself from cheating. You only need the dice to bounce, which is a much easier thing to arrange.
Put a handful of dice in a box, shake, and read them left to right. Use a box with enough room for the dice to tumble properly and shake it long enough that they do.
If that handful happens to be a mix of different dice, so much the better: different flaws partly cancel each other (§17). The gain is far too small to go shopping for, so just use whatever you happen to have, even if they all come from the same set.
Speed. Ten dice give you ten rolls per shake, so 99 rolls takes ten shakes instead of 99 separate throws.
Tumbling. A closed box bounces the dice off the walls and off each other, which is the part a hand throw has to get right and often does not. The box does it every time, with no technique to learn.
Dice nearly remove trust from seed generation. Not entirely, but what is left is narrow and specific rather than the broad act of faith that “trusting your hardware wallet” usually describes.
Check your device against at least one independent tool. Roll a full test set, enter it, and confirm that an independent implementation of the same method turns those rolls into the same words (§15). Do that a couple of times, with a fresh test set each time, before you roll the seed you mean to keep.
The rolls have to be a test set, because the ones used to create your real seed should never be typed into a second machine.
When a device turns your rolls into a phrase, it is asserting exactly one thing: that these words are what its published method produces from those digits. For the hashed construction of §8 that reads:
These words are the BIP-39 encoding of SHA-256 over the digits you just entered.
Every construction in this catalog substitutes its own formula, and none of them leaves anything for the device to choose, so the method and the rolls fix the answer between them.
That assertion is falsifiable: one correct answer, computable by anyone holding the rolls, depending on nothing the device knows. A device that shows you anything else either has a bug or is deliberately trying to deceive you. Either way, it is caught the moment you check.
The useful consequence is that you can check the device as often as you like, with test rolls. Nothing is at stake in a test set, so it can be done on any computer, connected, carelessly, as many times as your patience allows.
Roll a test set for real, exactly as you would one you meant to keep. Digits you made up instead of rolling are not a rehearsal of anything. They can trip the pattern guards in §14, so the run never exercises the path your real seed will take, and typing numbers you did not roll is not a habit to build. What makes a test run free is that the seed is disposable, not that the process is.
Each pass rules out a little more. A device that returns the expected answer run after run, for inputs it had no way to anticipate, is working from what you typed and nothing else.
A device could answer honestly through every test and then lie on the run that matters, but it would have to guess which run that is, and nothing in your rolls tells it.
After enough test runs the device is not something you trust. It is a tool you have checked, doing exactly what its method says it should.
It is the obvious move and it is a bad trade. To check the seed protecting your funds you have to type your real rolls into another computer and that computer now knows your seed. You would be exchanging a small residual doubt about a device you have tested for a fresh and unbounded exposure to one you have not.
Working offline does not fix this. An air-gapped machine can still keep what you typed and you have no way to know what it kept or who reads it later. The risk is not the network, it is the second copy.
That is what test runs are for: the seed that matters is entered once, in one place.
Two browser tools cover most of what is in this catalog: iancoleman.io and Seed Tool from bitcoiner.guide. Either one will turn a set of test rolls into words you can hold up against your device, and §15 walks through it click by click. For most people that is the whole job.
An AI can also write you a checker from scratch, sharing no lineage with either tool. §15 has a prompt to copy.
Not everyone thinks this trade is worth making. §20 sets out the strongest objection to dice and the 2026 disclosure that tested it.
Checking the arithmetic establishes that the device turned your rolls into the right words. It says nothing about whether the machine kept a copy of them, or whether the firmware on it is the firmware the vendor published. Those are separate questions, and they are not answered by trust either.
Where a project is open source, the code that turns your rolls into a seed can be read by anyone, including you. It is a short and unusually readable piece of any wallet: collect the digits, apply the published formula, encode the result.
You do not have to be a programmer to get something out of this. Ask an AI to find the dice code in your wallet's repository and explain what it does with your rolls, then ask it what else it does with them.
Reading the source tells you what the published code does. Three further steps connect that code to the device in your hand, and none of them asks you to take anyone’s word for it:
Each is a subject in its own right and this page does not try to teach any of them. What matters here is that they exist, and that using them is not someone else's job.
“Open source” is not the same as “reviewed.” The entropy defect in §20 sat in published code from 2021 until 2026 before anyone caught it. Public code protects you only when somebody actually reads it, and that has meant waiting for a specialist with the time to look.
That is the part that has changed. An AI will read a firmware repository alongside you and answer questions about what it does, which puts a real look at the code within reach of someone who could not have managed it alone. You need no permission and no expertise to point one at your wallet's dice routine and ask what it touches.
These methods are not interchangeable. Two implementations agree only when they use the same method.
Click any column heading for how that method works.
| What the implementation does with your rolls | ||||||||
|---|---|---|---|---|---|---|---|---|
| Hash the rolls as typed | Rewrite each 6 to a 0, then hash | Pack into bits, never hash | Read as a base-6 number | Completes a hand-built phrase | Vendor publishes a dice worksheet | Adds the device's own entropy | What you roll | |
| Signing devices and wallets, where seeds get generated | ||||||||
| BitBox02 | · | · | · | · | ●1 | ● | · | dice, coin |
| Blockstream Jade | · | · | · | · | ● | ●2 | · | 2 × D16, D8 |
| Coldcard | ● | · | · | · | ●3 | · | ●4 | dice |
| Foundation Passport | · | · | · | · | ◐5 | ·6 | ◐5 | your choice |
| Kern | ● | · | · | · | ●3 | · | · | dice |
| Keystone | · | ●7 | · | · | ? | · | · | dice |
| Krux | ●8 | · | · | · | ●3 | · | · | dice, D20 |
| SeedSigner | ● | · | · | · | ●9 | · | · | dice |
| SparrowDesktop software | · | · | · | · | ●10 | · | · | your choice |
| Specter DIY | · | · | · | · | ◐11 | · | ●11 | coin |
| Software tools and apps, for generating or independently checking one | ||||||||
| AirGap Vault | ●12 | ●13 | · | · | ? | · | · | dice |
| BlueWalletMobile app | · | · | ● | · | ? | · | ●14 | coin, dice, D20 |
| Gordian Seed TooliOS app | ●15 | · | · | · | ? | · | · | dice, coin |
| Gordian seedtool-cli | ●16 | · | · | · | ? | · | · | dice |
| iancoleman.io | ●17 | ●18 | ●18 | · | ? | · | · | dice |
| Seed Toolbitcoiner.guide | ●19 | · | ●19 | · | ? | · | · | dice |
| Libraries | ||||||||
| RooSoft/bitcoinlibElixir | · | · | · | ●20 | ? | · | · | dice |
| 24 more walletsTrezor, Ledger, and the rest of §13 | · | · | · | · | · | · | · | nothing |
● yes, read from source ◐ yes, with the limit in the note · no ? not established here
One column the chart does not carry: whether an implementation looks at the rolls you typed and tries to judge them. Most do not, and the few that try disagree about how. §14 is what each one checks and what the check is worth.
The chart records what each implementation does with your rolls, read from source. It is not an audit of the software and appearing in it is not a recommendation. §18 sets out what was established and what was not.
50 rolls makes a 12-word seed. 99 rolls makes a 24-word seed. The rolls are hashed as text, exactly as you wrote them down, and the resulting hash is the seed's entropy.
Check this method yourself: ten minutes, no programming, with the published test vectors.
Roll a die 99 times
This example follows a 24-word seed the whole way through.
Write the digits down
Hash the digits once with SHA-256
The quotation marks matter: the digits go in as text. The result is always 32 bytes, regardless of how long the input was. For a 12-word seed, only the first 16 are used.
Write those bytes out in binary
Each byte is eight bits. Nothing has changed yet, the same hash, written differently.
Collect the bits into groups of 11
Look each group up in the BIP-39 wordlist
01010001010→650+1651→eyebrow10011000101→1221+11222→obvious11011000011→1731+11732→such11011000111→1735+11736→suggestEach group is a number from 0 to 2047. The +1 accounts for printed wordlists, which start counting at 1 rather than 0. The final group carries a checksum.
Build the final word
6d, and 23 words have used all but its last 3 bits01101101sha256(entropy) =867da839c3…1000011010110000110→1414+11415→radioThis is why the last word cannot be chosen freely.
Read off the words
Any implementation of this method gives the same 24 words from the same rolls.
A die has six faces, so N rolls produce 6N possible sequences. The only question is whether that is as many as there are possible seeds.
| Possible dice sequences | Possible seeds | ||
|---|---|---|---|
| 50 rolls → 12 words | 650 ≈ 8.1 × 1038 | 2128 ≈ 3.4 × 1038 | 129.2 bits, over the 128 needed |
| 99 rolls → 24 words | 699 ≈ 1.1 × 1077 | 2256 ≈ 1.2 × 1077 | 255.9 of 256 bits |
50 rolls comfortably exceeds what a 12-word seed needs. 99 rolls lands at 255.9 bits against 256, a 100th roll would push past what a 256-bit seed can hold.
A correct implementation reproduces the published vectors in §15. If yours does not, the most likely cause is that it implements §9, §10, or §11 instead.
Coldcard also offers a separate mode that hashes your rolls on top of a seed the device generated. Nothing from that mode reproduces anywhere, because the starting value was never shown to you. It is a real trade rather than a mistake, protecting you if your dice turn out to be terrible, at the cost of the one property that made dice worth using.
The same shape as the hashed method of §8, over a different alphabet. Every 6 is rewritten to a 0 before hashing, so the six faces read as 0 to 5 rather than 1 to 6. Everything after that is identical: one SHA-256, truncate, encode.
Check this method yourself: the same tool as §8 with one setting changed and that setting decides which method you get.
One substitution, and it is not a subtle difference in the result. Run the 99-roll example from §8 through both and the two 24-word phrases have no words in common at all. They agree only if you roll no 6 in the entire sequence, which at 99 rolls happens about once in 69 million.
| Implementation | Where it applies and what else differs |
|---|---|
| Keystone Hardware | The whole dice path. Requires 100 rolls for 24 words rather than 99, hard-enforced, while its own documentation still says 99. The 24-word gate is gui_model.c L497. |
| iancoleman.io Dice mode | Applies the rewrite, then hashes only if you fix a word count. Its default sub-mode does not hash at all, which puts it in §10 as well. |
| AirGap Vault “Coldcard” mode | Applies the rewrite. Coldcard does not do this, so the setting does not do what its name says. |
AirGap Vault's dice screen offers a mode labeled “Coldcard”. It applies the 6-to-0 rewrite, which Coldcard's firmware does not. Choosing it because you own a Coldcard gives you a seed your Coldcard will never reproduce. Its other mode is the method in §8.
This method never hashes anything. Each face contributes bits directly and the packed bits are the seed entropy.
Check this method yourself: only against itself, because the implementations do not agree with each other.
Because six is not a power of two, the encoding is variable-length: four of the faces carry two bits each and the remaining two carry one, which is what keeps the result unbiased. A six-sided die therefore averages about 1.67 bits rather than the 2.585 an ideal encoder would extract, so filling a 24-word seed's 256 bits takes roughly 154 rolls. Needing more rolls than a hashed method is a property of this encoding, not a sign that your dice are worse.
| Implementation | What it does |
|---|---|
| BlueWallet Mobile app | Packs rolls into bits, 0-indexed, with no minimum roll count. Any shortfall is filled from the phone's RNG after a dialog, so a short session produces a seed that is partly the device's and reproduces nowhere. |
| iancoleman.io Dice mode, default sub-mode | Rewrites 6 to 0 first, then packs. The word count varies with how many times you rolled. |
| Seed Tool “Raw” mode | Packs rolls into bits without the rewrite, so its table and iancoleman's disagree with each other even though both are “raw dice.” |
No two of the three produce the same seed from the same rolls, which is worth stating plainly. iancoleman rewrites every 6 to a 0 before packing and the other two do not; BlueWallet and Seed Tool share a face-to-bits table but differ in what they do with a roll count that under- or overshoots. There is no shared convention here to check against, which §15 sets out as the one method where cross-checking cannot help.
The oldest and most literal reading: the roll string is one long number written in base 6, so the digits are parsed as an integer and written back out as bytes. No hash anywhere.
Check this method yourself: the conversion is checkable with a pencil on a short sequence.
It is internally consistent and it is not broken. It is also the method most likely to look correct from outside while producing something else entirely.
The Elixir library RooSoft/bitcoinlib demands exactly 50 or 99 rolls, the counts that everywhere else in this catalog signal a hashed construction (§8), then reads the digits as a base-6 number and never hashes them. Everything visible from outside matches. The seed does not.
SeedSigner itself used this method until February 2022, along with the 6-to-0 rewrite, which is why old rolls do not reproduce old seeds on a current release. The release history in the companion analysis gives the formula for each era. §15 has the steps for checking the conversion.
A whole group of implementations does no dice arithmetic at all and still deserves to be called dice support. The method is old and needs no firmware: roll dice, look each word up in a printed table, write it down. Anything that accepts a typed recovery phrase can then receive the result.
Check this method yourself: the lookup table is the only thing that can be wrong and checking it needs nothing but the wordlist.
What makes it practical is the last word. A BIP-39 phrase ends in a checksum, so most word combinations are invalid, and finding a legal final word by hand means testing candidates one at a time. These implementations compute it for you, which is the whole reason the feature exists.
A printed table is the usual way to reach the first 23 words, but it is not the only one. Coin flips do the same job eleven bits at a time and a desktop wallet can offer the same calculation against words you arrived at however you like, which is what puts Sparrow here.
Two different things are worth separating and the chart gives them separate columns. One is whether a device will finish a phrase you built yourself. The other is whether its vendor publishes the worksheet that makes the dice route practical, which is a statement about what the vendor recommends rather than about what the device can do.
Only BitBox02 and Jade do both. The rest of this group has the capability without the worksheet, which means the method works there but you supply the table.
| Implementation | What the device contributes | Worksheet the vendor publishes |
|---|---|---|
| BitBox02 | Shows the 8 valid final words; you choose | A dice how-to and a 4-page lookup table |
| Blockstream Jade | Shows the valid final words; you choose | A 17-page guide: two 16-sided dice and one 8-sided, one word per throw |
| Foundation Passport | Picks the final word itself, using the device's RNG | None. Documents the feature and names dice, but advises against it |
| Sparrow Desktop software | Autosuggests every checksum-valid candidate as you type the last word, since 1.7.4 | None for dice. Ships a Border Wallets grid and its own docs describe neither feature |
| Specter DIY | An 11-bit keypad sets any word by hand and the checksum is repaired. Words you do not set stay device RNG | None. Its documented input is coin flips, not dice |
| SeedSigner | A Calc Final Word tool: enter 11 or 23 words, then give the spare bits as coin flips, a word you pick, or zeros | None |
| Coldcard, Krux, Kern | Offer only checksum-valid choices for the last word of a phrase you type in | None. The capability falls out of ordinary seed entry |
BitBox02, Jade, and Passport built the final-word feature deliberately for hand-made seeds, and two of the three publish the lookup table to go with it. BitBox02's source says so in a comment: the special case exists “so that users can generate a seed using only the device and no external software, allowing seed generation via dice throws.”
But the same affordance turns up elsewhere without being advertised. All four devices that implement the hashed method of §8 will also complete a hand-built phrase, verified from source in each case: Coldcard, Krux, and Kern by restricting the last word during ordinary seed entry, SeedSigner through a dedicated tool built for word or coin-flip entry rather than for dice.
So if you have a printed table and one of those devices, the worksheet method works. You are using a capability rather than following a supported procedure and the difference matters mostly in where you get the table and who has checked it.
BitBox02's table is the clearest published example of the method. It calls for five ordinary dice and a coin, rerolling any die that shows a 5 or a 6. Twenty-three words later you have 253 bits and the device supplies the last word.
Jade's published method is not a six-sided one at all. It calls for two 16-sided dice and one 8-sided die, thrown together once per word and read off a lookup table, with no rerolling and no coin flip needed. It arrives where BitBox02's method arrives, by different hardware and different arithmetic. The two are not variants of one scheme and a transcript from one means nothing to the other.
Worth noticing: the final 3 bits of a 24-word seed come from a human choosing among 8 candidates, not from the dice. Negligible against 253, but the dice do not supply everything.
Both Passports hand those 3 bits to the device's RNG instead, so the last word is not yours at all. That is the difference between their half marks in the chart and BitBox02's full one.
The worksheet method never hashes anything, so it reproduces none of the machine constructions in this catalog. A BitBox02 dice transcript does not even contain the digits 5 or 6. The phrase you end up with is ordinary BIP-39 and works in any wallet; it is the route from dice to phrase that does not transfer.
In exchange it is the only method here you can carry out with a pencil. The arithmetic that needs a machine has been reduced to one word.
The table is the hard part of this method and it does not have to come from a wallet vendor. Several people have published their own. These were surveyed on 2026-08-15 and anyone can publish another, so read this as the ones found rather than the ones that exist.
| Worksheet | What it calls for | How it covers all 2048 words |
|---|---|---|
| DiceBIP39 | Ordinary dice | One roll picks half the chart, five more are read as 1 to 4, rerolling any 5 or 6 |
| bip39_dice | D16, D8, D4, or a coin | Four tables. Each die contributes whole bits, so the throw is the word number in binary |
| Bip39-diceware | A coin and four ordinary dice | Heads reaches the first 1296 words, tails the remaining 752, rerolling tails above 4362 |
| SeedPicker | 342 paper tickets and one die | Ticket and die give 2052 cells; the four spare ones say draw again |
| RudeFox | Any source of bits | A grid of 128 rows and 16 columns, read as 11 bits |
Every one of them addresses all 2048 words evenly, which is the property that matters and the one worth checking. Each construction was verified against the wordlist for this page; the survey records the pinned sources, the file hashes, and how far each table was checked. §15 gives the steps for checking a table yourself, which is worth doing before trusting any of them.
Three of the five print a word with the wrong capitalization. reardencode's four tables print
TRUE where the wordlist has true, RudeFox prints TRUE and
FALSE, and SeedPicker prints March and October.
These are spreadsheet artifacts: a sheet coerced true into a boolean and
autocapitalized two month names. reardencode's README says outright that its tables were built in
Google Sheets.
Nothing follows from it. BIP-39 words are lowercase, you will type the
lowercase word, and an implementation handed True rejects it as not being in the
wordlist rather than deriving anything from it.
The defect worth checking a table for is the other kind: a wrong word that is still a real BIP-39 word, which nothing downstream would question and which would substitute silently. None turned up in any table surveyed, though the survey records how far each one was checked.
Take the dice out of this method and it still works. Draw the words themselves from a complete physical set, one at a time, and the device completes the phrase exactly as it does from a worksheet. What changes is the selector, not the method.
SeedPicker is the case where both are present at once: you draw a paper ticket and roll a die, the pair addressing one word between them.
That technique has its own reference: Drawing BIP-39 words, covering what makes a draw fair, what goes wrong with the set itself, and the sets available. It is a work in progress.
codex32 (BIP-93) is the other worksheet-driven scheme that belongs in this conversation and it is a much larger subject than a paragraph. It is left out because its output is a BIP-32 master seed rather than a BIP-39 phrase, so nothing in this catalog applies to it. The BIP carries a section titled “Not BIP-0039 Entropy” explaining that choice.
A worksheet is specific to one wordlist and a finished phrase does not say which list produced it. SLIP-39 uses 1024 words rather than 2048. Of those, 553 are also BIP-39 words sitting at different positions. Electrum is the sharper case: its English wordlist is the BIP-39 list byte for byte, but a version 2 Electrum seed is validated by a different checksum entirely, and Electrum discards any seed of its own that would also pass as BIP-39.
Everything on this page ends in a BIP-39 phrase.
These generate seeds from their own RNG and offer nothing for dice beyond the ordinary ability to type in a phrase you produced elsewhere:
Trezor1 · Ledger2 · KeepKey · OneKey · Bitkey · Tangem · Cypherock · NGRAVE · Ellipal · SafePal · D’CENT · Satochip · Prokey · SecuX · Arculus · Ballet · Portal · Frostsnap · Electrum · Nunchuk3 · Liana · Wasabi · Blockstream Green · Bitcoin Core4
Whichever construction an implementation uses, it faces a separate problem: someone who types the same digit fifty times, or an obvious pattern, rather than rolling. Implementations differ on whether they try to catch that at all, and the differences do not follow the method they use.
| Implementation | What it checks |
|---|---|
| Krux | Live Shannon-entropy meter against the 128/256-bit threshold, arithmetic-pattern detector, distribution graph. All overridable |
| Coldcard | Rejects if any single face exceeds 30% of rolls when generating from dice. No check on the path that mixes rolls into an existing seed |
| Keystone | Warns if any single face exceeds 30% of rolls, but the warning is advisory and Confirm stays live |
| Seed Tool, SeedSigner, Kern, RooSoft/bitcoinlib | Enforces the roll count and nothing else |
| AirGap Vault | Nothing beyond the 99-roll minimum, plus an explicit refusal to hash an empty string |
| iancoleman.io | No minimum. Warns that the phrase “will appear more secure than it really is” and generates it anyway |
| Gordian Seed Tool | Nothing and no roll count either |
| Gordian seedtool-cli | Nothing, no roll count, and it will derive a seed from five rolls |
| BlueWallet | Nothing and no minimum. Under-rolling silently tops up from the phone's RNG after a dialog |
| BitBox02, Jade, both Passports, Sparrow, Specter DIY | Not applicable. These never see your rolls, so there is nothing for them to check |
Roll-count enforcement is the guard that has actually mattered. One roll leaves six candidate seeds and two leaves 36, and wallets sitting in a space that small get found and emptied.
This is not hypothetical. In October 2023 Crypto Guide went looking for exactly those wallets, searching the chain for seeds built from low dice-roll counts, reported that funds had already been taken from them, and demonstrated the same thing still being possible on a Coldcard Mk4 running the firmware current at the time. (A separate and older problem than the 2026 RNG failure in §20.)
Both common approaches, a face-frequency limit and a Shannon-entropy score, measure which faces appeared. Neither sees what order they appeared in.
| What you typed | Shannon | Top face | Coldcard 30% | Compression |
|---|---|---|---|---|
666666… | 0.000 | 100% | caught | caught |
121212… | 1.000 | 50.5% | caught | caught |
123123123… | 1.585 | 33.3% | caught | caught |
123456123456… | 2.584 | 17.2% | passes | caught |
| a real fair sequence | 2.562 | 20.2% | allowed | allowed |
Rolling 1-2-3-4-5-6 repeatedly gives every face its exact fair share, so it scores essentially maximum while being entirely predictable. A compression check sees it, because repetition compresses whether or not the distribution looks uniform.
Krux is the partial exception: alongside its Shannon meter it runs a derivative-based check that flags arithmetic progressions, so it catches that row where the scores alone do not.
With only 50 or 99 samples the estimate is far too noisy. Tuned to warn 1 honest user in 1,000, a Shannon score at 99 rolls catches a die favoring one face 50% more often just 4.9% of the time. Coldcard's fixed 30% limit shows the same tension from the other side, falsely warning 3.4% of honest users at 50 rolls. §17 has the thresholds and the arithmetic.
It resolves itself, because these checks miss exactly the biases that do not matter: a bias small enough to hide in 99 rolls is far too small to cost a seed anything (§2). A roll-quality guard should catch patterned input, not judge your dice. Compression does that better than either approach, with fewer false alarms.
What you can check depends on which method your wallet uses, and each method's own section links into its walkthrough here. The worksheet route of §12 needs nothing but the wordlist. Anything ending in a hash needs a machine, and what matters is that the machine is not the device you are checking.
Use the published vectors or test rolls of your own. Do not enter the rolls or words of a seed holding real funds into any second tool, offline included. Every copy is another place it can leak from, and an air-gapped machine keeps what you type just as well as a networked one.
Everything worth learning here is learnable from test rolls. See §5.
Ten minutes, no programming. This reproduces §8.
655152231316521321611331544441236164664431121534415633526456254462245546236542364246312613322234612| Value | |
|---|---|
| 50 rolls → 12 words | |
| rolls | 65515223131652132161133154444123616466443112153441 |
| SHA-256 | 6cb09af855050dcde6fe2adc3181c250982011e2cf17821cbed56a908ec527c3 |
| entropy (16 B) | 6cb09af855050dcde6fe2adc3181c250 |
| mnemonic | hole luggage safe present express tragic orbit shed switch metal identify path |
| 99 rolls → 24 words | |
| rolls | 655152231316521321611331544441236164664431121534415633526456254462245546236542364246312613322234612 |
| SHA-256 / entropy | 51531761ec7a738946e0b9f46bb11320a695495430e345c14f01ad8b3b898a6d |
| mnemonic | eyebrow obvious such suggest poet seven breeze blame virtual frown dynamic donor harsh pigeon express broccoli easy apology scatter force recipe shadow claim radio |
There is no BIP for this. What follows is not a proposal for one. It is a description of what the nine implementations already do, written down so it can be checked against them.
INPUT rolls : string of ASCII digits '1'-'6'
len(rolls) == 50 -> 12-word seed
len(rolls) == 99 -> 24-word seed
STEP 1 entropy = SHA256(rolls.encode('ascii')) # 32 bytes
STEP 2 if 12-word: entropy = entropy[0:16] # 128 bits
STEP 3 mnemonic = bip39_encode(entropy) # BIP-39, English
# indices are 0-based: 'abandon' == 0
NOT no 6 -> 0 remap; the digit 6 is hashed as the character '6'
no base-6 or numeric conversion of the roll string
no bit-packing or bias-correction encoding
no separators, padding, or trailing newline in the input
Ten minutes, no programming. This reproduces §9, the construction Keystone uses and the one behind AirGap Vault's “Coldcard” mode.
655152231316521321611331544441236164664431121534415633526456254462245546236542364246312613322234612Setting 24 words makes the page report “The mnemonic will appear more secure than it really is” and rate the rolls as good for only 15 words. Nothing has gone wrong, and the seed is not weak.
That warning is measured against the tool's own bit-packing conversion, which reads a die at about 1.69 bits and turns 99 rolls into 167 bits: enough for 15 words, short of the 256 that 24 words need.
Fixing a word count is precisely what takes that conversion out of the picture.
In that state the page hashes the digit string instead. Your 99 rolls carry 255.9 bits into that
hash. The scoring above the field is describing a path the code did not take.
The
source hashes entropy.cleanStr when a length is set, while the warning tests
entropy.binaryStr.length.
The same rolls in Hex mode raise no warning at all, because hex reads 4 bits a character, so 99 characters convert to 396 bits and clear the threshold outright.
If the phrase comes out different, check the word count before anything else. Leaving Mnemonic Length unset is the common failure here: the tool switches to packing bits without saying so, and returns a perfectly valid phrase built by a different method.
This checks the construction rather than any particular device. A Keystone will not accept the vector at all, because it enforces 100 rolls for 24 words rather than 99.
§10 cannot be cross-checked at all. The three implementations map faces to different bit strings and one rewrites a 6 first, so there is no shared answer to compare against. The most you can establish is that an implementation reproduces itself: enter the same rolls twice and confirm the phrase comes back the same.
§11 is checkable in principle and tedious in practice. Reading 99 digits as one base-6 integer and writing it back out as bytes is machine work. Do it once on a short sequence with a pencil, because the point worth seeing is what is absent: no hash appears anywhere in the conversion.
The worksheet method needs no arithmetic, which leaves the table itself as the only thing that can be wrong. What you are looking for is a cell holding a real BIP-39 word that belongs at a different index, since nothing downstream would question it. The capitalization artifacts found in three of the five worksheets in §12 are harmless by comparison.
2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda.TRUE, FALSE, and capitalized month names. Harmless in
themselves, but they are what a spreadsheet does to true, false,
march and october, so one of them tells you the table was never
compared against the wordlist.Both reach all 2048 words exactly, by different arithmetic.
BitBox02 5 dice, each rerolled until it shows 1-4 -> 2 bits per die = 10 bits
1 coin flip -> 1 bit
11 bits per word; 23 words = 253 bits; the device supplies word 24
Jade 2 x D16 + 1 x D8, thrown together once per word
index = (D1-1)*128 + (D2-1)*8 + (D3-1)
16 x 16 x 8 = 2048 exactly: no rerolling, no coin flip
A table that fails any of these was never compared against the wordlist by whoever published it. The survey records how far each one was checked here.
Two of the implementations in §7 are browser tools and the
walkthroughs in this section reach for one of them. Each ships a signed standalone build alongside
the hosted page. iancoleman's releases
carry
bip39-standalone.html beside signature.txt.asc, and
Seed Tool's carry
index.html beside signature.txt.
No real rolls ever go into any of these tools. They exist for one purpose: to establish that a device turns rolls into the seed the published method says it should. The rolls you use to create a seed you intend to keep are entered on that device and nowhere else, as §5 sets out.
The hosted pages are fine for that. A test run uses rolls you are discarding and vectors that are already public, so there is nothing for a server to learn.
Downloading the signed build is an optional extra. A hosted page can serve different code on every visit and can serve it to you alone, so in principle a checker could be changed under you and made to agree with a device that is wrong. A file you have checked against its signature is the same code every time you open it. That is a precaution against a narrow attack rather than a prerequisite for checking anything.
The goal is a checker you can run, not an answer from a chatbot. Copy the prompt below as it stands, run the program it gives you on your own machine, and keep your rolls out of the conversation.
I want a small Python program I can run offline that turns dice rolls into a
BIP-39 recovery phrase, so I can check what my hardware wallet produced.
Start by reading this page:
https://kdmukai-bot.github.io/seedsigner-ai-analysis/dice/standard.html
Wallets do not agree on how to turn rolls into a seed. Section 7 charts which
implementation uses which method, and sections 8 to 12 describe each method.
1. Ask me which wallet or tool I am using.
2. Look it up in that chart and tell me which method it uses before you write
any code. If it is the bit-packing method, stop and say so: no two
implementations of that one agree, so there is nothing to check against.
3. Implement BIP-39 from the specification. Do not import a wallet library.
4. Prove the encoding is right by reproducing the official BIP-39 test
vectors, and show me that before going further.
5. Implement my wallet's method exactly as that page describes it, then
reproduce the published test vector for it from section 15.
Do not judge the result by testing whether the output looks random. Hashing
always produces output that passes randomness tests, so that proves nothing.
Step 4 is not optional. A model that cannot encode a known entropy value correctly cannot tell you anything about your rolls, and a wrong one will be fluent and confident about it.
Step 2 is what stops it guessing. Making the model name your wallet's method before it writes any code heads off the common failure, which is applying the 6-to-0 remap unprompted: iancoleman's Dice mode is well represented in training data and looks like the obvious reading of “dice entropy”. The other failure is reaching for a wallet library rather than the specification, which quietly reintroduces the shared lineage you were trying to escape.
The backing material sits in the repository beside this document, so none of it has to be taken
on description alone. Only verify_constructions.py is standard library only; the rest
need pip install mnemonic embit==0.8.0 in a virtual environment.
| File | What it establishes |
|---|---|
evidence/verify_constructions.py | Standard library only. Reproduces five official BIP-39 vectors as a self-test before asserting anything, then recomputes both vectors in §8, the competing 6-to-0 construction from §9, and the worksheet mapping from §12. Run it from evidence/; it reads the wordlist from the working directory. |
evidence/wallet-dice-survey.md | The source reading behind the chart in §7, with the pinned commit each classification came from, the third-party worksheet survey, and the method any further wallet has to be surveyed by. |
dice_bias_analysis.py | The min-entropy figures in §2. |
dice_shannon_scoring.py | Why the Shannon-scoring guard discussed in §14 does not catch a bad die. |
dice_tendice_box.py | The dice-in-a-box pool-size sweep in §2. |
Published analyses of these implementations that we are aware of. Where one reports figures, they were reproduced independently before it was listed.
3238f6fd, which is the emergency release of
31 July 2026), run on the headless simulator, logging
every write into the hash accumulator and confirming no RNG is touched. Includes a
deterministic-build proof tying that commit to Coinkite's published signed firmware.
By Portland.HODL.
This list reflects what we know of, not what exists. An implementation missing from it has not been shown to be unreviewed, only that we are unaware of a published analysis. If you know of one or write one, it belongs here.
Appendix
Everything above is the document. What follows is what it rests on: the measurements behind the dice figures, where every mark in the chart came from, and how to establish a mark for a wallet that is not in it yet, and the strongest case against the whole exercise. It is written to be checked rather than read, which in practice means a model checking it. Pointing one at these sections is the fastest way to audit a claim or add a row.
The route in for a human is the source table: every implementation, with the pinned commit its classification was read at.
The figures in §2 are other people's measurements, converted into seed bits here. This section is the detail behind them: what was measured, by whom, how thin the evidence is, what the conversion assumes, and where the thresholds they are judged against come from.
Only one of the two measurements is per-face. Labby's gives a full distribution and can be converted into seed bits directly, but it rests on twelve dice that all shared the same defect in the same direction, which is what a single mold produces. Twelve dice from one bag are not twelve independent samples of the world's dice.
Iversen's has the sample size Labby's lacks, 58 cheap dice tested individually, but records only odd against even, so its 1.44% is a per-face figure only under the assumption that the excess is spread evenly across the three even faces. That assumption follows from the mechanism they propose and is not measured.
Why that assumption does not carry much weight. Min-entropy reads only the single most likely face, so the number of elevated faces never enters the arithmetic. A die with one face 2% high and a die with all three even faces 2% high are worth the same 253.1 bits at 24 words. Iversen's pattern, at 1.44% a face, costs less than either at 253.9. The 2% working figure bounds the odd-even result however the excess turns out to be distributed, which is what makes it safe to quote a per-face number from a study that never measured one.
What is missing is a modern per-face study across many brands and many individual dice. We are not aware of one. What makes the gap tolerable is that the conclusion is insensitive to the number: Robustness to the estimate being wrong applies the same conversion to a ladder of worse dice, and the bias has to reach several times either measurement before a 24-word seed loses even five bits.
Zacariah Labby, “Weldon's Dice, Automated”, CHANCE 22(4), 2009. An apparatus of solenoids, a webcam, and pip-counting software repeated Weldon's 1894 experiment: 26,306 throws of 12 dice, 315,672 individual rolls. The dice are described in the paper as “inexpensive, standard white plastic dice with hollowed-out pips and a drop of black paint inside each pip.”
| Face | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| Observed | 0.1686 | 0.1651 | 0.1662 | 0.1658 | 0.1655 | 0.1688 |
| Fair | 0.1667 | 0.1667 | 0.1667 | 0.1667 | 0.1667 | 0.1667 |
-5, -3, -1, 1, 3, 5 trend. Fitting that pattern gives p = 0.00005 against it. Labby
then measured the dice with a micrometer and found the 1–6 axis consistently about
0.2% shorter than the 2–5 and 3–4 axes. The dice are not quite cubes, and the
1 and 6 faces are correspondingly larger.W. F. R. Weldon reported 26,306 throws of 12 dice to Francis Galton in a letter dated 2 February 1894, recording only whether each die showed a five or six. Karl Pearson used the data in the 1900 paper that introduced the chi-square goodness-of-fit test.
Iversen, Longcor, Mosteller, Gilbert, and Youtz, “Bias and runs in dice throwing and recording”, Psychometrika 36(1), 1971. Willard Longcor threw 219 dice, one block of 20,000 throws each, 4,380,000 throws, recording only whether the up-face was odd or even.
The dice are described but the brands are not named. The paper calls them A, B, C, and X:
| Brand | Type | Size | Dice |
|---|---|---|---|
| A | Precision | 0.77 in | 100 |
| B | Precision | 0.71 in | 30 |
| C | Precision | 0.75 in | 31 |
| X | “the usual inexpensive drilled plastic dice” | 0.61 in | 58 |
The precision dice are described as having “sharp edges meeting at 90° angles” with pips “either lightly painted or constructed from extremely thin disks, the whole object being polished beautifully,” shipped in plastic cases in foam-cut slots, and costing about $1.25 a pair at the time. Brand X is the five-and-dime-store die with drilled pips and a drop of paint in each hole.
The papers disagree about why cheap dice are biased, which is worth knowing before citing either as the explanation.
Iversen attributes brand X's bias to pip weight: the even faces carry 2+4+6 = 12 pips against the odd faces' 1+3+5 = 9, so drilling them out leaves the even faces lighter and they land up more often. Labby tested exactly that prediction on his dice and rejected it, then found a dimensional cause instead, the 1–6 axis being 0.2% short.
Both can be true of different dice and the two studies are blind to each other's defect. Iversen's paper says so itself: a bias where “the probabilities of opposite sides are equally inflated and other opposites equally deflated, as they might be if the die were a rectangular parallelepiped instead of a cube” would not be detected by odd-even recording. A short 1–6 axis raises faces 1 and 6 together, one odd and one even, so it cancels in exactly that way.
What survives both is the magnitude. Two experiments, two mechanisms, two eras, both landing near 1.4%.
Most modern dice-fairness testing is about twenty-sided dice and it does report widespread failure. Campbell and Miller at Western Kentucky University, whose 2024 abstract opens “Dice are cursed or blessed; that is, they roll low or high, but they are never fair,” report that roughly one plastic-mold die in four tests fair over 3,000 rolls.
Two things to hold onto before importing that conclusion here. A twenty-sided die has far smaller faces and far more of them, which the same authors note makes it more susceptible to manufacturing imperfection than a d6. And failing a fairness test is a statement about detectability at a given roll count, not about effect size: it says the bias is big enough to see, not that it is big enough to matter to a seed.
Sourced from the published abstracts rather than the full papers, which were not retrievable. The related finding that a die's chi-square correlates with the spread of its measured diameters is consistent with Labby's micrometer result, and is worth checking properly if this section is ever revised.
The usual alternative to rolling, and the one the hobby recommends: dissolve salt into warm water until a die floats, then spin it repeatedly and watch whether the same face keeps surfacing. It is a real test, and it is far quicker than the roll counts in the conversion. It also answers a narrower question than it appears to.
None of that changes the advice, because the advice never depended on bias being hard to find. A die whose bias needs salt water to reveal is a die whose bias costs a seed nothing.
The claim in §4 that a mixed handful beats a matched set is
ours, not a measurement, and it is computed by
dice_tendice_box.py. Each of 4,000 trials draws ten
dice whose per-face weights are perturbed by a gaussian of a given severity; a pool of n reads the
first n of those ten, so the columns compare the same dice rather than independent redraws.
Min-entropy in bits per roll, against 2.585 for a perfect die.
| Worst face | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Gain |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 2% unfair | 2.558 | 2.566 | 2.569 | 2.571 | 2.573 | 2.574 | 2.575 | 2.575 | 2.576 | 2.576 | +0.019 |
| 6% unfair | 2.495 | 2.521 | 2.533 | 2.540 | 2.544 | 2.548 | 2.551 | 2.553 | 2.555 | 2.556 | +0.061 |
| 13% unfair | 2.412 | 2.461 | 2.483 | 2.496 | 2.505 | 2.512 | 2.518 | 2.522 | 2.526 | 2.529 | +0.117 |
| 26% unfair | 2.265 | 2.349 | 2.390 | 2.413 | 2.430 | 2.443 | 2.453 | 2.461 | 2.467 | 2.472 | +0.207 |
M. Kapitaniak, J. Strzalko, J. Grabski and T. Kapitaniak, “The three-dimensional dynamics of the die throw”, Chaos 22(4), 047504, 2012. A three-dimensional model of a die bouncing on a table with dissipation, compared against high-speed camera observation.
The figures quoted in §4 are from their basin-of-attraction analysis for a cubic die, where they define the average probability that the die lands on the face which was lowest at the start:
These are model results checked against experiment, not a throw-count measurement, which is a weaker kind of evidence than the dice-fairness counts. The direction of the effect, that insufficient bouncing preserves the starting orientation, is the robust part.
Why §4 does not convert this into seed bits. The probability is conditional on the starting orientation. If that orientation is itself uniformly random, the marginal distribution over faces stays uniform and no entropy is lost. The loss appears only through correlation: if the way you handle the die between throws makes each starting position a function of the previous result, the rolls stop being independent, and it is the independence rather than the per-roll distribution that the seed arithmetic assumes. We are not aware of a published measurement of how strong that correlation is for real human rolling and that is the honest gap here.
This part is ours and it is the part to attack.
dice_bias_analysis.py carries the measured
distribution as a literal and prints every figure quoted in §2.
h = -log2(max p_face) # min-entropy per roll, the worst case
bits = min(seed_size, num_rolls * h) # SHA-256 treated as an ideal extractor
measured worst face 0.1688 -> h = 2.566 bits/roll
24-word: min(256, 99 * 2.566) = 254.1 -> costs 1.9 bits
12-word: min(128, 50 * 2.566) = 128.0 -> costs nothing, 50 rolls carry 128.3
2% rounded up, used in §2 -> h = 2.556 bits/roll
24-word: min(256, 99 * 2.556) = 253.1 -> costs 2.9 bits
12-word: min(128, 50 * 2.556) = 127.8 -> costs 0.2 bits
fair face 0.16667 -> h = 2.585 bits/roll
24-word: 255.9 12-word: 128.0
Every row below is the same conversion applied to a worse die. This is the table that makes the thin evidence base tolerable: the answer does not turn on getting the estimate right.
| Worst face runs | Bits per roll | 24-word seed | 12-word seed |
|---|---|---|---|
| 1.0% high | 2.571 | 254.5, costs 1.5 | 128.0, costs nothing |
| 1.3% high, the measurement | 2.566 | 254.1, costs 1.9 | 128.0, costs nothing |
| 2% high, what §2 uses | 2.556 | 253.1, costs 2.9 | 127.8, costs 0.2 |
| 3% high | 2.542 | 251.7, costs 4.3 | 127.1, costs 0.9 |
| 5% high | 2.515 | 248.9, costs 7.1 | 125.7, costs 2.3 |
| 8% high | 2.474 | 244.9, costs 11.1 | 123.7, costs 4.3 |
Where the chart's lines are actually crossed. Against a fair share of 16.7%, a
24-word seed does not fall to 128 bits until one face lands 40.8% of the time, and
does not reach the 112-bit floor until 45.7%. A 12-word seed, carrying half as much, reaches that
floor at 21.2%. All four are printed by
dice_bias_analysis.py.
What would change this. A modern per-face study across many brands and many individual dice. We are not aware of one and its absence is the honest gap in §2. If you know of one or run one, the conversion is three lines and the ladder can be re-anchored immediately.
The chart in §2 draws two lines. Neither is ours.
112 bits, the dashed line. NIST SP 800-57 Part 1 Rev. 5, Table 4: a security strength below 112 bits is disallowed for applying cryptographic protection, 112 is acceptable through 2030, and the accompanying text sets the expectation at “at least 128 bits in 2031.” Earlier drafts of this page used a 100-bit “practical floor” instead, which was invented rather than cited.
128 bits, the solid line. It is where NIST arrives in 2031, and it is also the point past which a Bitcoin key stops getting stronger. Pieter Wuille, on the recurring 12-or-24-words question, first the claim:
“cryptographically speaking, there is no benefit to private keys having more than 128 bits of entropy, as long as there is no pattern in them (e.g. don't use just 128 bit numbers as private keys, that's broken, but using the hash of 128 bit numbers appears to be just fine). The reason for suggesting more is just defense in depth.”
Then the reason:
“24 words = 256 bits, 12 words = 128 bits. But finding a private key given the public key already costs only 2128 operations (using e.g. Pollard's rho algorithm, not using brute force of course). So if an attacker can just attack the private keys that come out directly, there is no reason to try to find the seed.”
The guards in §14 score the rolls the user just typed, which is a
sample of 50 or 99. That is far too small a sample to characterize a die, and both common guard
designs inherit the problem. Computed by
dice_shannon_scoring.py and
dice_coldcard_crosscheck.py, both seeded, so
they reproduce exactly.
A fair die does not score 2.585. A Shannon estimate over a short sample runs low, by about (K−1)/(2N ln 2), so a threshold has to sit below the fair-die average by enough to clear its spread as well:
| Fair-die average | Spread to the 0.1st pct | Usable threshold | |
|---|---|---|---|
| 99 rolls | 2.548 | 0.109 bits | 2.439 |
| 50 rolls | 2.511 | 0.232 bits | 2.279 |
Each was read from source at a pinned commit, not from documentation, except where the method is a document: the worksheet procedures are first-party PDFs and help articles, cited as such.
This is a source read, not an audit. Each row records the arithmetic an implementation applies to your rolls, traced in the code at the commit pinned for it in the table that follows. The code around it was not reviewed and nothing in a row is a judgment about the software as a whole.
Nothing here was run on the hardware. The constructions were checked by recomputing them with an independent BIP-39 encoder written for the purpose and validated against the official test vectors. That establishes what the published code does, not what a particular build on a particular device does.
Sharing a column is not a promise that two implementations interchange. They agree only where the roll count and the word length agree too. Several are narrower than their row suggests: Krux matches on its D6 path and not its D20 one, Seed Tool on its hashed mode and not its raw one, Gordian Seed Tool for iOS at 12 words with no 24-word path at all. The survey carries the qualification row by row.
And some rows cannot be source-verified by anyone outside the vendor. Ledger's onboarding code is closed, Nunchuk's iOS repository returns 404, and seedtool.info's dice behavior sits inside a WASM build that was not disassembled.
Coldcard is here because its construction is the one several of these projects converged on and because it is the implementation this document checks the hashed method against. That is a statement about one hashing routine, not about the device. After the entropy failure disclosed on 1 August 2026, set out in §20, this document treats Coldcard as a reference for the construction and as part of its history, not as a live option for new storage.
The same separation applies to every row. What is recorded here is what an implementation does with your rolls. Nothing about that speaks to the rest of a device, its firmware, or the parts of it a dice roll never touches.
| Implementation | Language | Source |
|---|---|---|
| Signing devices | ||
| SeedSigner | Python | SeedSigner @ 0.8.7 and tools_views.py for the final word |
| Coldcard | MicroPython | Coldcard/firmware @ 3238f6fd |
| Krux | MicroPython | selfcustody/krux @ 7ea3f95 |
| Kern | C | odudex/Kern @ 0.0.15 and manual_input.c for the final word |
| Keystone | C | keystone3-firmware @ 3.0.2 |
| BitBox02 | Rust and a PDF | bitbox02-firmware @ v9.26.4, plus the vendor's lookup table |
| Blockstream Jade | C and a help article | Blockstream/Jade @ 1.0.40, plus its dice article and the 17-page guide it links |
| Foundation Passport | MicroPython and Rust | Core: passport2 @ v2.3.11. Prime: KeyOS @ v1.3.1, a separate codebase reaching the same behavior |
| Sparrow | Java | sparrowwallet/sparrow @ 2.5.3 |
| Specter DIY | MicroPython | specter-diy @ v1.10.4 |
| Software tools and apps | ||
| AirGap Vault | TypeScript | airgap-vault @ aa50b7f |
| Gordian Seed Tool | Swift | GordianSeedTool-iOS @ 1.6 |
| Gordian seedtool-cli | C++ | BlockchainCommons/seedtool-cli |
| Seed Tool | JavaScript | BitcoinQnA/seedtool, web or signed download |
| iancoleman.io | JavaScript | iancoleman/bip39 @ de71c22, web or signed download |
| BlueWallet | TypeScript | BlueWallet @ 8.0.1 |
| Libraries | ||
| RooSoft/bitcoinlib | Elixir | RooSoft/bitcoinlib @ a998a61 |
The full reading, including the wallets that turned out to offer nothing and the traps that
produce wrong answers in both directions, is in
evidence/wallet-dice-survey.md beside this
document.
This section is written for whoever adds the next row, which in practice is often a model. Pointing one at this section and at the chart in §7 is the fastest way to get a wallet classified correctly and the method is short enough to be worth reading yourself if you want to know how much a row is worth.
A row has to answer seven questions, one per column: which of the four constructions the implementation applies to your rolls, whether it will complete a phrase you built by hand, whether its vendor publishes the worksheet that method needs, and whether anything the device generated is blended in.
Both of these produced wrong answers in the survey behind this document and each was caught only after the wrong answer had been written down.
Searching firmware for dice arithmetic, finding none, and concluding “no dice support” misclassifies BitBox02, Jade, and Passport. All three ship a feature built for hand-made seeds and two of the three publish dice instructions themselves. Their firmware contains no dice arithmetic because the arithmetic is the user's job. The feature is a lookup table in a PDF plus a checksum solver in the device.
Reading the seed-generation path, finding a call to the system RNG, and stopping there misclassifies Sparrow. Its last-word calculator sits on the seed-entry path, not the generation path, and its own documentation describes neither of the two features that put it in the chart. The affordance is not always where the entropy is and it is not always written down.
dice drowns in false positives. The substring
dominates inside indices, which appears in the hundreds in several of these repositories.
BIP-39's own English wordlist contains the word dice. Italian, Spanish and
Portuguese localization files carry codice, and índice, and Spanish
que dice means “which says.”The only method that gets both directions right is to read the seed-creation path, the release notes, and the vendor's own documentation. Any one of the three alone will be wrong about some wallet in the chart.
Two negative findings in §13 show the standard being applied to itself: Ledger's onboarding firmware is closed source and Nunchuk's iOS app is closed, so both entries rest on documentation rather than a source read, and both say so.
Not everyone thinks the trade is worth it. Foundation, who make Passport, ship the final-word feature but advise against dice. Their then-head of strategy argued publicly in 2024, in a personal post on a third-party forum rather than as company policy, that they had heard of zero compromised seeds caused by hardware-wallet RNG failures, against repeated cases of users losing funds to botched dice procedures, one of them having used fewer than ten rolls.
The half about botched procedure stands and it is the real objection: dice move the risk onto the human, and humans skip steps. The other half was an argument from absence of evidence, and the evidence arrived. On 1 August 2026 Wizardsardine disclosed that Coldcard had shipped a broken entropy path since 2021, across the Mk2, Mk3, Mk4, Mk5, and Q. The defect has been fully verified and wallets were drained at scale. Coinkite issued emergency firmware on 31 July, which fixes generation from that point on and repairs nothing already generated.
The disclosure draws one exception, in these words: “Coinkite sets the exception at at least 50 fair, independent and private rolls, and considers such a seed not at risk from this RNG issue alone.” That is this document's argument, tested against the largest hardware-wallet entropy failure to date and coming through it. A seed the device chose was compromised by a defect its owner had no way to see. A seed its owner rolled was not.
All three qualifiers are things this page already asks of you. Fair is §2, where the answer is that the dice you already own qualify. Independent is the thorough tumble of §4. Private the disclosure glosses itself: “nobody watched, filmed or logged the rolls, because entropy that someone else saw is not entropy any more.”
The exception is narrow and worth stating exactly. It covers the seed, not the device. The disclosure is explicit that other features remain broken and that dice rolls do not protect you there.
The procedural half of that objection is undiminished and the specific failure it describes is one the better implementations already prevent, by refusing to proceed below the required roll count.
Substantive changes made after publication are recorded here, with what changed and why. Revisions made while drafting are not corrections and are not listed.
generate_mnemonic_from_coin_flips exists in
helpers/mnemonic_generation.py at 0.8.7 and implements the hashed construction over a
binary alphabet, but it has no caller anywhere in the source or the tests, so no menu reaches it.
The live coin-flip affordance is the Calc Final Word tool, which uses coin flips for the last
word's spare bits through a different function. The §7 row now reads
dice only, and its note records the unreachable routine. The chart was reporting a capability the
code has and the product does not.