Most players who click “verify” on a dice game are trusting a second piece of the casino’s own code to tell them the first piece was honest. That is better than nothing, but it is not verification. If you want to verify a provably fair dice roll properly, you need to recompute it yourself, outside the casino’s interface, from the raw inputs. The maths is short: one SHA-256 hash, one HMAC-SHA256 call and a few lines of integer arithmetic.

This guide walks through the commitment scheme, the inputs, the conversion from hex digest to a roll between 0.00 and 99.99, and a worked example whose every value was computed in Python and cross-checked in Node.js. It ends with what a successful check does not tell you, which is where most misunderstandings live.

Key takeaways

  • The casino commits to a secret server seed by publishing its SHA-256 hash before you bet. You check the commitment after the seed is revealed.
  • Each roll is derived from HMAC-SHA256(server_seed, “client_seed:nonce”). The nonce counts your bets under one seed pair.
  • A common conversion reads the digest five hex characters at a time, skips any value of 1,000,000 or more, then takes the value mod 10,000 and divides by 100.
  • You can only verify rolls from a seed that has been rotated and revealed. An active seed is unverifiable by design.
  • A matching roll proves that one outcome followed the published formula. It says nothing about house edge, payouts or licensing.

The commitment scheme: why the hash comes first

Provably fair dice is a commit-and-reveal protocol. Before your first bet, the casino generates a random server seed and shows you only its hash. SHA-256, specified in NIST’s FIPS 180-4 Secure Hash Standard, is designed so that nobody can find a second input with the same output, and nobody can work backwards from the output to the input. Publishing the hash therefore locks the casino into one seed without revealing it.

The hiding property protects the casino: you cannot predict rolls while the seed is active. The binding property protects you: the casino cannot swap the seed after seeing your bets, because any other seed would produce a different hash. When you rotate the seed pair (usually a button in the fairness settings), the old server seed is revealed and a new hashed seed takes its place.

Commitment alone is not enough, because the casino chose the seed. If it also chose every other input, it could in principle search for a seed that produces bad rolls for a predictable sequence of bets. That is what the client seed is for.

Server seed, client seed and nonce

The client seed is a string you control. Most sites generate a default, but you should replace it with your own value before betting, after the server seed hash is already shown. Because the casino committed to its seed before knowing yours, it cannot tune one against the other.

The nonce is a counter that starts at 0 (some sites start at 1) and increases by one with every bet under the current seed pair. It lets a single seed pair produce thousands of independent rolls without re-seeding.

The three are combined with HMAC, the keyed hash construction defined in RFC 2104 and standardised by NIST in FIPS 198-1. The server seed is the key, and the message is the client seed, a colon and the nonce. Using HMAC rather than a plain hash of concatenated strings avoids ambiguity about where one input ends and the next begins, and gives a well-studied pseudorandom function. If you want the primitive itself explained from first principles, see our SHA-256 explainer, and for implementation details in JavaScript our walkthrough of HMAC-SHA256 in Node.js.

Seed state Server seed Client seed Nonce What you can verify
Active (before or during play) Hidden, hash shown Visible, editable Increments per bet Nothing yet; save the hash
Rotated Revealed in plain text Fixed for that pair Final count recorded Hash match plus every roll in the pair
Rotated but unrevealed Still hidden Fixed Recorded Nothing; treat as a red flag
New pair New hash shown New value (set it yourself) Reset to 0 Nothing yet; save the new hash

From hex digest to a dice roll

HMAC-SHA256 outputs 32 bytes, written as 64 hexadecimal characters. A dice game needs a number from 0.00 to 99.99, which means 10,000 equally likely outcomes. The conversion used by many Bitcoin dice games works like this:

  1. Take the first 5 hex characters of the digest and read them as an integer. Five hex characters span 0 to 1,048,575.
  2. If the value is 1,000,000 or more, discard it and take the next 5 characters. Repeat as needed.
  3. Take the accepted value mod 10,000 and divide by 100. The result is the roll.
  4. In the astronomically unlikely case that all 12 full chunks are rejected, the roll is 99.99.

Step 2 is the part people skip, and it is the part that keeps the game unbiased. The range 0 to 999,999 contains exactly 100 copies of every remainder mod 10,000, so every roll is equally likely. If the casino used the full 0 to 1,048,575 range, remainders 0 to 8,575 would appear 105 times and the rest only 104 times, a small but real skew. Rejecting values above the threshold is standard rejection sampling. Any one chunk is rejected with probability 48,576 / 1,048,576, about 4.6%.

Not every casino uses this exact recipe. Some read four bytes into a float, some apply the house edge inside the formula, some hash rather than HMAC. The method must be published, and you should verify with the formula the casino documents for that game version. A useful starting point is a guide such as Best Bitcoin Casino, which lists crypto dice games alongside the verification tools each one publishes, so you can find the documented formula before you play rather than after.

Worked example with real values

Here are inputs you can reproduce on your own machine. Every value below was computed with Python’s hashlib and hmac modules and checked again with Node’s crypto module.

  • Server seed (revealed): b7e3c91f4a2d8e6015f9c3a7d4e8b2f6
  • Commitment shown before play, SHA-256 of that seed: 7ada29d216a1cf6cca75a5849c603173135d74fd707eb8f2ffa83773e971ee52
  • Client seed: shattered-demo-42
  • Nonce: 0, so the HMAC message is shattered-demo-42:0

Step one, check the commitment. Hash the revealed server seed as a UTF-8 string. If the result does not equal the hash you saved before play, stop: nothing else matters.

Step two, compute HMAC-SHA256 with the server seed as key and shattered-demo-42:0 as message:

0e0ddd910a0f996b25ef4ef8c53c7435f7ab84879fec89f71fe7b50b0cf780ac

Step three, convert. The first five characters are 0e0dd, which is 57,565 in decimal. That is below 1,000,000, so it is accepted. 57,565 mod 10,000 is 7,565, and 7,565 / 100 gives a roll of 75.65.

The same seed pair produces the following rolls for the next nonces. Nonce 38 is included because it shows the rejection rule in action: its first chunk fcef4 is 1,036,020, which is over the threshold, so the verifier moves on to 1d3ea (119,786) and the roll is 97.86.

Nonce Digest (first 10 hex) Chunk used Decimal Roll
0 0e0ddd910a 0e0dd 57,565 75.65
1 9b9795252a 9b979 637,305 73.05
2 89cc002700 89cc0 564,416 44.16
3 dbe0848a71 dbe08 900,616 6.16
4 10d80bedc2 10d80 68,992 89.92
5 02d0583e0a 02d05 11,525 15.25
38 fcef41d3ea 1d3ea (fcef4 rejected) 119,786 97.86

A 15-line Python verifier

The script below uses only the standard library (see the Python hmac documentation). It refuses to produce a roll unless the revealed seed matches the commitment, which is the order a careful verifier should work in.

import hashlib, hmac
def verify(server_seed, server_hash, client_seed, nonce):
    if hashlib.sha256(server_seed.encode()).hexdigest() != server_hash:
        raise ValueError("Revealed seed does not match the commitment")
    msg = f"{client_seed}:{nonce}".encode()
    digest = hmac.new(server_seed.encode(), msg, hashlib.sha256).hexdigest()
    for i in range(0, len(digest) - 4, 5):
        value = int(digest[i:i + 5], 16)
        if value < 1_000_000:
            return digest, round((value % 10_000) / 100, 2)
    return digest, 99.99

print(verify("b7e3c91f4a2d8e6015f9c3a7d4e8b2f6",
             "7ada29d216a1cf6cca75a5849c603173135d74fd707eb8f2ffa83773e971ee52",
             "shattered-demo-42", 0))

Running it prints the digest from the worked example and the roll 75.65. Change the nonce argument to 38 and it returns 97.86. Then swap in your own seeds and nonces from a real session.

Pitfalls that break a verification

Seed rotation timing. You can only verify a seed pair after rotating it. Players who never rotate never get a revealed seed, so their “provably fair” history is unverifiable. Rotate at the end of every session you care about.

Unrevealed seeds. If a site rotates your pair but does not show the old server seed, or shows a seed that does not hash to your saved commitment, the guarantee has failed. Screenshot or copy the hash before you play; a hash you only see in the casino’s history page can be rewritten along with everything else.

Encoding and formatting. Most failed checks are formatting errors: a trailing space in the client seed, hex seeds treated as raw bytes instead of text, a nonce starting at 1 rather than 0, or a dash instead of a colon. Use exactly the message format the casino documents.

Algorithm changes. A seed commitment does not commit to the conversion formula. Our analysis of whether a provably fair casino can change its algorithm explains why you should save the documented formula and version alongside your seeds.

House edge is separate. The roll can be perfectly fair while the payout table is not generous. A “roll under 50” bet that pays 1.98x carries a 1% house edge whatever the seeds say.

What provably fair does not prove

A matching roll establishes that one outcome came from the committed seed, your client seed and the stated nonce under one formula. It does not establish:

  • RTP or house edge. These come from the payout multipliers, not the random number generator.
  • Correct settlement. The roll may be right and the credited win wrong. Check the bet record, not only the roll.
  • Withdrawals. Cryptography cannot make an operator pay out.
  • Licensing and oversight. Regulators such as the UK Gambling Commission set their own remote gambling and software technical standards, including independent testing of random number generation. A self-verifiable hash is a complement to that kind of oversight, not a replacement.

Provably fair dice turns one claim, “this roll was not rigged after you bet”, into something you can check with fifteen lines of code. Everything else about a casino still has to be judged the old way. Gamble only where it is lawful for you, only if you are of legal age, and only with money you can afford to lose. In the UK, the National Gambling Helpline is 0808 8020 133; in the US, call 1-800-GAMBLER.

Frequently asked questions

Can I verify a dice roll before the server seed is revealed?

No. While the seed is active, you only have its hash, and the hash cannot be reversed. Rotate the seed pair to reveal the old server seed, then verify every roll made under it.

Why does the verifier skip values of 1,000,000 or more?

Five hex characters cover 0 to 1,048,575. Only the range 0 to 999,999 divides evenly into 10,000 outcomes, so rejecting higher values keeps every roll from 0.00 to 99.99 equally likely.

Does it matter which client seed I use?

Its value does not need to be clever, but it should be yours and set after the server seed hash is displayed. That ordering is what stops the casino from choosing a server seed tuned to your inputs.

If my roll verifies, is the casino fair?

Only that roll is shown to follow the published formula. The house edge, payout accuracy, withdrawal record and licence all need separate checks.

Sources

  • NIST, FIPS 180-4 Secure Hash Standard: csrc.nist.gov/pubs/fips/180-4/upd1/final
  • NIST, FIPS 198-1 The Keyed-Hash Message Authentication Code (HMAC): csrc.nist.gov/pubs/fips/198-1/final
  • IETF, RFC 2104 HMAC: Keyed-Hashing for Message Authentication: datatracker.ietf.org/doc/html/rfc2104
  • Python Software Foundation, hmac module documentation: docs.python.org/3/library/hmac.html
  • UK Gambling Commission, Remote gambling and software technical standards: www.gamblingcommission.gov.uk/standards/remote-gambling-and-software-technical-standards