DEV Community

CryptoPlay Guide
CryptoPlay Guide

Posted on Originally published at cryptoplay.guide

Reproduce a Provably Fair Result with HMAC-SHA256

A “provably fair” label becomes useful when you can turn it into a reproducible check. For a developer, the interesting part is the path from a previously published commitment to an exact game result: hash the revealed input, rebuild the HMAC message, then apply the documented byte conversion.

That path has several distinct failure points. Getting the cryptography right is necessary, but the result also depends on the nonce, message format and game formula. A matching digest with the wrong result mapping is an incomplete verification.

The walkthrough below uses example seeds created for the original CryptoPlay Guide article. They are not records of a casino bet. The Dice and Limbo mappings follow the published formulas described in that article; another operator may use different rules.

Treat the commitment as your starting input

Before betting, the operator shows a SHA-256 hash of a secret server seed. Save that hash. The client seed is visible, as is the nonce that identifies the bet within the seed pair. The operator reveals the old server seed after you rotate seeds.

You therefore need the revealed server seed, the hash saved before play, the client seed and the exact nonce. You cannot complete this check while the active server seed remains secret.

Here is the example server seed:

cryptoplayguide-example-server-seed-2026
Enter fullscreen mode Exit fullscreen mode

The pre-bet commitment for this example is:

ea1e9ffa82b568d4ecb93488031e79bfe0cba227dbbe2b134c73e66079e78e91
Enter fullscreen mode Exit fullscreen mode

Use this command to check it:

printf %s "cryptoplayguide-example-server-seed-2026" | sha256sum
Enter fullscreen mode Exit fullscreen mode

Compare the entire output hash with the saved commitment. A mismatch means the revealed seed does not match that commitment; the later result calculation cannot repair that failed check.

Preserve the input exactly. A trailing newline or space changes the hash, which is why the example uses printf %s rather than echo.

Rebuild the HMAC message explicitly

For this Dice and Limbo example, the server seed is the HMAC-SHA256 key. The message has the format client_seed:nonce:round, with round set to 0.

Using client seed my-client-seed and nonce 1, the command is:

printf %s "my-client-seed:1:0" | openssl dgst -sha256 -hmac "cryptoplayguide-example-server-seed-2026"
Enter fullscreen mode Exit fullscreen mode

It produces:

d6e37d4cd754f8752a46ef3f8c7bcd60434a1abe8a9ab09594833e9b21883b86
Enter fullscreen mode Exit fullscreen mode

The placement of each input matters. Do not swap the key and message or silently change the message separators. Likewise, copy the nonce from the bet record: some operators start counting at 0, while others start at 1.

At this stage you have reproduced the digest. You still need to reproduce the game result.

Follow the byte mapping, then the payout mapping

Take the first four bytes of the digest:

d6 e3 7d 4c
Enter fullscreen mode Exit fullscreen mode

Those bytes are 214, 227, 125 and 76. Convert them as follows:

214/256 + 227/256² + 125/256³ + 76/256⁴ = 0.83940871…
Enter fullscreen mode Exit fullscreen mode

For Dice, apply:

floor(value × 10001) / 100
Enter fullscreen mode Exit fullscreen mode

The example result is 83.94.

For Limbo with the example's 1% edge, apply:

max(1, floor(0.99 / value × 100) / 100)
Enter fullscreen mode Exit fullscreen mode

The corresponding result is 1.17×. These are different mappings from the same underlying value. Copying a Dice conversion into a different game does not establish that game's result.

For another useful check, keep the seeds and change the nonce to 2. The message becomes my-client-seed:2:0; the digest begins ad06e622, the value is 0.67588652…, and the Dice result is 67.59.

Inspect the implementation without confusing game models

The open-source verifier is available at:

https://github.com/cryptoplayguide/provably-fair-verifier

Its README describes a dependency-free JavaScript module for browsers or Node.js 18+ using the Web Crypto API. It checks commitments and implements the documented Dice and Limbo mappings. Its Limbo implementation rejects a float of zero, or a value outside (0, 1), with a RangeError rather than returning Infinity.

The repository also supports a Crash model, but its inputs and byte handling differ: salt is the HMAC key, the game hash is the message, and the calculation takes the first 52 bits. That distinction is a reason to read the operator's fairness documentation before choosing a verifier mode.

A browser interface is available at https://cryptoplay.guide/tools/provably-fair-verifier/. CryptoPlay Guide states that verification runs locally in the browser and sends nothing to CryptoPlay Guide. You can first reproduce the example, then use your own revealed inputs.

Keep the conclusion narrow

A successful check establishes that the supplied result matches the committed seed under the formula you used. It does not establish solvency, withdrawal reliability, licensing or an attractive house edge.

Keep result verification and payout analysis as separate checks. Reproducing a roll explains where that roll came from; it does not make a negative expected return disappear.

Originally published at https://cryptoplay.guide/guides/verify-provably-fair-step-by-step/

CryptoPlay Guide, the site this was originally published on, earns affiliate commissions from some casinos it covers. This post contains no affiliate links.

18+ (or the higher legal age where you live). Gambling involves risk of loss; only play where legal, set deposit, loss and time limits, and seek help if gambling causes harm, for example from BeGambleAware.org.

Top comments (0)