Provably fair4 min read

How to verify a provably fair bet: a step-by-step check

A worked check of one dice bet with real numbers: compare the seed with its SHA-256 commitment, recompute the HMAC and turn bytes into a roll.

Fact-checked 18 October 2026 · 5 sources

This guide checks one dice bet from start to finish with real numbers, using two tools most computers already have or can get for free: Node.js and OpenSSL. The seeds below are made up for the example, so you can reproduce every value. For a bet of your own, swap in the seeds from your seed history.

What you need

A check needs four inputs and one recorded result. If the terms are new, the provably fair explainer covers what each one is for.

InputExample value
Commitment shown before betting39c1b59ade7f924729700fbd928027114342547b2ba223afcee423ab81e38023
Server seed, revealed after rotatingb4cda9f8db189736c5099b1dde3109d7d17d2dc77ca1439e3f72329bc54b4878
Client seeddust2_mid_doors
Nonce of the bet3 (the fourth bet with this pair, since nonces start at 0)

The example server seed was generated with crypto.randomBytes(32) and written as 64 hex characters, the same shape cs2.casino uses.

Step 1: rotate the seed pair

While a pair is active, its server seed is secret. Rotating the pair reveals it, and from then on every bet made with that pair can be checked. Your seed pair and its history are under Your seeds on the provably fair page.

Note the commitment before you rotate. The point of the check is to compare the revealed seed with a hash you saw before the result existed.

Step 2: check the commitment

Hash the revealed server seed with SHA-256, the function specified in NIST's FIPS 180-4. In a terminal:

  • printf '%s' 'b4cda9f8db189736c5099b1dde3109d7d17d2dc77ca1439e3f72329bc54b4878' | openssl dgst -sha256

The output is SHA2-256(stdin)= 39c1b59ade7f924729700fbd928027114342547b2ba223afcee423ab81e38023, which matches the commitment. The label before the digest differs between OpenSSL versions (macOS's built-in LibreSSL prints the digest alone); the hex is what counts. On a Mac, shasum -a 256 reading the same input prints the same digest, a second check that doesn't depend on OpenSSL.

Use printf '%s', not echo. Echo adds a newline, and a hash of the seed plus a newline is a different hash entirely: 84eb6c6fcf1e7a785cd4ea391e7fdb995b3f5212b4517bf0f518e76dad476eb9. On cs2.casino the seed is hashed as the 64-character text you see, not as the 32 bytes the hex stands for.

Step 3: recompute the HMAC

The randomness for a bet comes in 32-byte blocks: block k = HMAC-SHA256(server seed, "client seed:nonce:k"). A dice roll needs only block 0, so the message is dust2_mid_doors:3:0, and the key is the server seed text.

With OpenSSL, whose -hmac option takes the key as a string:

  • printf '%s' 'dust2_mid_doors:3:0' | openssl dgst -sha256 -hmac 'b4cda9f8db189736c5099b1dde3109d7d17d2dc77ca1439e3f72329bc54b4878'

With Node.js, start node and type these lines one at a time:

  • const crypto = require('crypto')
  • const server = 'b4cda9f8db189736c5099b1dde3109d7d17d2dc77ca1439e3f72329bc54b4878'
  • crypto.createHash('sha256').update(server).digest('hex')
  • const h = crypto.createHmac('sha256', server).update('dust2_mid_doors:3:0').digest()
  • h.toString('hex')

Both print the same block: 0363b9d22f5ecdf5ac9d34ccec54776efb6155e224aae74a88f8e2f023e54ad3.

If you want to test your setup first, use a published answer. HMAC-SHA256 with the key key and the message The quick brown fox jumps over the lazy dog is f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8, an example listed on Wikipedia's HMAC page. Both tools reproduce it.

Step 4: turn four bytes into a float

Each block holds eight floats of four bytes each. The first float uses the first four bytes, 03 63 b9 d2, which are 3, 99, 185 and 210 in decimal. The formula is float = b0/256 + b1/256² + b2/256³ + b3/256⁴:

| Term | Value | |---|--:| | 3 / 256 | 0.01171875 | | 99 / 65,536 | 0.0015106201171875 | | 185 / 16,777,216 | 0.0000110268592834 | | 210 / 4,294,967,296 | 0.0000000488944352 | | Sum | 0.013240445870906115 |

That is the same as reading the four bytes as one big-endian number, 0x0363b9d2 = 56,867,282, and dividing by 2³² (4,294,967,296). In Node: h.readUInt32BE(0) / 2 ** 32. Every float is a whole number of 2⁻³² steps, so any correct tool gets exactly the same value.

Step 5: turn the float into a dice roll

The dice rule is roll = floor(float × 10000) / 100. Here 0.013240445870906115 × 10000 = 132.404…, the floor is 132, and the roll is 1.32. In Node: Math.floor(h.readUInt32BE(0) / 2 ** 32 * 10000) / 100.

A roll can be any of 10,000 values from 0.00 to 99.99. Over wins when the roll is above your target and under when it is below, so an under 50.00 bet wins with this roll and an over 50.00 bet loses. The dice odds article explains how the target sets the win chance and the multiplier.

Step 6: compare with the record

The last step is to compare 1.32 with the roll recorded for the bet. A match means this result came from the seed committed before the bet, your client seed and that nonce, through the published rule. The calculator on the provably fair page does steps 2 to 5 in your browser when you paste the revealed seed, client seed and nonce; the terminal route is useful because it doesn't depend on any code the casino wrote.

Games that need more floats

Dice reads one float. Other games read more, in order: floats 0 to 7 come from block 0, floats 8 to 15 from block 1 (message ending in :1), and so on. A 16-row Plinko drop reads 16 floats, one per row, where floor(float × 2) is left or right. A blackjack hand reads a 52-card shoe, seven blocks in total. The rules for every solo game are listed under how single-player games work.

Crash and roulette rounds are checked differently, from a hash chain and a public beacon instead of your seeds. The drand article covers that check.

When the numbers don't match

Most mismatches are input mistakes. Before concluding anything, check these:

  1. A trailing newline or space in the seed or message (use printf '%s').
  2. The wrong nonce: nonces count from 0, so a pair's first bet is nonce 0.
  3. A changed client seed: use the one that was active for that bet.
  4. The hash used as the HMAC key instead of the revealed seed.
  5. The seed decoded from hex into bytes instead of used as text.

If the commitment in step 2 really doesn't match the revealed seed, or a clean recomputation gives a different result, keep the evidence: the commitment, the revealed seed, the client seed, the nonce and the bet's record. That combination is what shows a result wasn't produced the way the site says.

Sources