How Provably Fair Works: Step by Step
A detailed technical walkthrough of the provably fair mechanism — server seed commitment, client seed, nonce, HMAC hashing, result derivation, and post-bet verification — with pseudocode examples.
Provably fair gambling is built on a classical cryptographic primitive called a commitment scheme: one party commits to a value by publishing evidence of it before an event, then reveals the value afterward so anyone can confirm it matches. Combined with a player-supplied random input and a deterministic outcome function, this creates a system where neither the casino nor the player can manipulate the result — and either party can verify it after the fact.
This article walks through the full mechanism phase by phase. The pseudocode shown here reflects the approach used by most major provably fair casinos and is accurate in structure; exact implementations vary slightly by platform.
Phase 1 — Server Seed Generation and Commitment
Before a session begins, the casino’s server generates a cryptographically secure random value called the server seed. This is typically 64 hexadecimal characters (256 bits of entropy), produced by a cryptographically secure pseudorandom number generator (CSPRNG).
server_seed = CSPRNG(256_bits)
# Example: "a3f8c2d19e4b7f06a1c3e5d7b9f2a4c6..."
The casino does not give you the server seed directly — that would let you predict the outcome before placing the bet. Instead, it publishes a hash of the server seed:
server_seed_hash = SHA256(server_seed)
# Example: "e3b0c44298fc1c149afbf4c8996fb924..."
This hash is your commitment receipt. Because SHA-256 is a one-way function, the hash reveals nothing about the underlying seed — but you can later verify that any revealed seed matches it. If the casino tries to change the server seed after you bet, it would have to produce a different input that produces the same hash — an attack known as a preimage attack, for which SHA-256 has no known practical vulnerability.
Phase 2 — Client Seed
You supply a client seed: a string you choose (or that the casino pre-populates and lets you change). This is your contribution to the randomness. Its purpose is to ensure the casino cannot have pre-calculated the outcome for your specific session — the server seed alone produces a deterministic result, but only once your client seed is known.
client_seed = "my_custom_seed_string_123"
Most casino interfaces let you change your client seed at any time between bets, which rotates you to a new set of outcomes. Changing the client seed also triggers the casino to reveal the previous server seed, so you can verify all bets made under it.
Phase 3 — The Nonce
Because a single session may involve thousands of bets, and you shouldn’t have to negotiate new seeds every round, a nonce (number used once) increments automatically with each bet. The same seed pair produces a different outcome at each nonce value, giving you an unlimited sequence of unique results without renegotiating seeds.
nonce = 0 # increments: 0, 1, 2, 3, ...
Phase 4 — Computing the Outcome
The three inputs are combined using HMAC-SHA256 (Hash-based Message Authentication Code). HMAC takes a key and a message and produces a 256-bit output. Most implementations treat the server seed as the key and the client_seed:nonce concatenation as the message:
raw_output = HMAC_SHA256(
key = server_seed,
message = client_seed + ":" + nonce
)
# raw_output is 64 hex characters = 32 bytes = 256 bits
This output is deterministic — the same inputs always produce the same output — but unpredictable without knowledge of the server seed.
Converting the Hash to a Game Outcome
The raw 256-bit output must be mapped to a game-specific number. A common approach extracts the first meaningful integer from the hex string and maps it to the game’s outcome range:
# Take the first 4 bytes (8 hex chars) as an unsigned 32-bit integer
hex_chunk = raw_output[0:8] # e.g. "1a2b3c4d"
int_value = hex_to_int(hex_chunk) # e.g. 439,804,749
max_32bit = 4_294_967_295 # 2^32 - 1
# Normalize to [0, 1)
float_val = int_value / (max_32bit + 1) # e.g. 0.10236...
# Scale to dice range [0, 100)
dice_roll = float_val * 100 # e.g. 10.236
If the first chunk produces an integer outside the desired range (which can happen with modulo arithmetic for non-power-of-two ranges), many implementations cascade to subsequent 4-byte chunks until a valid result is found. This avoids modulo bias.
# Cascade example: try chunks until valid result found
for i in range(0, 32, 4):
chunk = raw_output[i*2 : i*2+8]
value = hex_to_int(chunk)
if value < USABLE_MAX:
return map_to_outcome(value)
Phase 5 — The Bet and the Reveal
You place your bet. The outcome is calculated server-side using the above process and applied to your wager. You see whether you won or lost.
When you rotate your client seed or the server chooses to rotate its seed, the casino publishes the previous server seed in plaintext. This is the reveal step:
revealed_server_seed = "a3f8c2d19e4b7f06a1c3e5d7b9f2a4c6..."
Phase 6 — Verification
You now have everything you need to audit any bet from the completed session:
# Step 1: Confirm commitment
assert SHA256(revealed_server_seed) == previously_published_hash
# Step 2: Recompute result for any specific bet
bet_nonce = 42
raw_output = HMAC_SHA256(revealed_server_seed, client_seed + ":" + bet_nonce)
dice_result = derive_outcome(raw_output)
# Step 3: Compare with casino's reported result
assert dice_result == casino_reported_result_for_nonce_42
If all three assertions pass, you have confirmed the casino did not alter the server seed for that session and reported the mathematically correct outcome. If step 1 fails, the casino changed its seed — a detectable fraud. If step 3 fails, the casino reported a wrong outcome — also detectable fraud. See how to verify a provably fair bet yourself for a practical guide using real tools.
Why HMAC Instead of Simple Concatenation?
A naive implementation might compute SHA256(server_seed + client_seed + nonce). HMAC-SHA256 is preferred for structural reasons: it is specifically designed to prevent length-extension attacks, where an attacker who knows a SHA-256 hash can append data and compute a valid hash for the extended input without knowing the key. While this attack vector is more theoretical than practical in this setting, HMAC is the correct tool and its use signals a competent implementation.
How This Differs From On-Chain Randomness
This entire mechanism is off-chain: the server seed is generated and stored on the casino’s servers, not on a blockchain. The blockchain is used for deposits and withdrawals, not for the randomness itself.
True on-chain randomness schemes — such as Chainlink VRF (Verifiable Random Function) — generate and verify randomness entirely on-chain, using cryptographic proofs that can be verified by any blockchain node. Block-hash-based randomness (using a future block hash as entropy) is an older and weaker approach with known manipulability by validators. These on-chain approaches are used by some blockchain-native games and smart contract casinos; they offer different trust properties and different verification mechanics. For a detailed treatment, see commit-reveal and randomness schemes.
The Limits of This System
The mechanism is sound and correctly implemented by reputable platforms. But it has structural limits that no implementation can overcome:
- The house edge is not verified. The outcome conversion function determines how results map to payouts. An operator can set any edge they choose in that mapping.
- The client seed entropy is yours to manage. A predictable client seed doesn’t break the system (the server seed still provides unpredictability), but some edge-case attacks have been theorized around weak client seeds in poorly designed implementations.
- Verification is opt-in. The mechanism only delivers its anti-fraud value to players who actually verify. Most do not.
- Third-party games are excluded. Slots, live dealer tables, and licensed third-party content use separate RNG systems and are not covered by the casino’s provably fair scheme.
Understanding the mechanism in full is the first step toward using it properly. Knowing where it stops is equally important.