ToolsGambling
TG
Provably Fair RNG Explained: Seeds, Bytes and Checks

How Provably Fair RNG Turns Seeds into Verifiable Results

Contents

Provably Fair is a verification protocol around deterministic random-output generation: once the required inputs are revealed, you can repeat the declared calculations and compare the result with the game history. It is not a promise of profit. The Provably Fair introduction explains the concept; this guide follows the technical chain from commitment hash to HMAC, bytes, a number and a game-specific result.

Two checks matter. Commitment verification establishes whether the revealed server seed matches a previously saved hash. Result replay establishes whether the historical round's inputs produce the recorded outcome. The second check requires the exact provider protocol, its version and its output-mapping rules.

How RNG differs from Provably Fair

A random number generator produces random or pseudorandom values; Provably Fair makes a declared method of producing an outcome verifiable. They are complementary concepts, not competing alternatives.

MechanismWhat it doesWhat it does not establish by itself
Random number generator (RNG)A general term for mechanisms that produce random valuesSource quality, unpredictability or the ability to verify a particular result
Pseudorandom number generator (PRNG)Deterministically expands an initial state into a sequenceCryptographic security: not every PRNG is secure, but not every PRNG is weak
Cryptographically secure generatorIs designed to resist prediction when its initial-state and implementation requirements are metCorrect implementation or sufficient unpredictability of the initial secret
HMAC-based streamProduces reproducible bytes from a key and messagesCompliance with HMAC_DRBG merely because HMAC is called repeatedly
Provably FairConnects a prior commitment, disclosure and outcome verificationUnbiased input selection, payment or protection against every attack

HMAC_DRBG is a specific construction with defined state, instantiation, generation and reseeding operations. An arbitrary HMAC loop is not enough to establish compliance with NIST SP 800-90A Rev. 1.

Security also depends on the source of the server seed. It needs sufficient entropy—unpredictability in the underlying secret—and must remain secret while active. A neat-looking distribution does not replace either condition. For example, UKGC RTS 7 addresses both statistical quality and unpredictability. That is a jurisdiction-specific standard, not evidence that any operator mentioned here has been certified.

Why use both a hash and HMAC?

The hash commits to the string that will later be revealed; HMAC uses a secret key to produce reproducible bytes. These are different operations, and neither is decryption.

In our example, the commitment hash is SHA-256 of the server seed. Save it before betting, then calculate SHA-256 again after the seed is revealed and compare the complete hashes.

HMAC-SHA256 takes a key and a separate message. Here, the server seed is the key, while the message combines the client seed, nonce and HMAC block counter. The keyed construction is defined in RFC 2104.

A hash obtained only after a bet does not establish that the commitment existed beforehand. Saving it at the right time is part of verification.

Which inputs generate the byte stream?

This particular scheme needs the server seed, client seed, nonce, HMAC block counter and exact payload serialization. If the game starts reading at a particular position, you also need its byte-stream cursor.

The documented Stake implementation uses HMAC-SHA256 with the server seed as the key and a message shaped as clientSeed:nonce:block. This supports the byte-generation stages below; it is not an audit of the deployed casino or confirmation of current Stake Dice settlement rules.

InputPurposeOur synthetic example
Server seedSecret HMAC key; enables replay after disclosuretg-rng-demo-server
Client seedAdditional input in the messagereader-example
NonceDistinguishes calculations in a sequence7
HMAC block counterSelects a 32-byte block0
Byte-stream cursorDetermines the block and the offset within it0, the beginning of the first block
Exact payload serializationSpecifies encoding, separators and field orderLiteral UTF-8 string reader-example:7:0
Commitment hashCommits to the server seed for later comparisonThe full SHA-256 value shown below

One HMAC-SHA256 block contains 32 bytes, enough for eight non-overlapping four-byte values. In the documented implementation:

currentRound = floor(cursor / 32)
withinBlockOffset = cursor % 32

Here, currentRound selects the block and withinBlockOffset selects a byte inside it. Cursor 32 therefore means block 1, offset 0—not nonce 32.

Encoding matters. The literal UTF-8 string ab is different from the single byte decoded from hexadecimal ab. Spaces, newlines, separators and field order also affect the calculation.

Do not assume every provider uses this scheme. BGaming describes a Result + Secret protocol, while Gamba converts mnemonic strings to binary data. Neither should be replaced with our literal UTF-8 serialization.

An exact example: from a server seed to 6.73

Our synthetic inputs produce four initial HMAC bytes that convert to 0.06736633484251797; under our explicitly declared teaching Dice rule, that becomes 6.73. This is not a casino wager or a verified current Stake Dice result.

The teaching rule is:

floor(u * 10000) / 100

It defines 10000 bins with displayed values from 0.00 to 99.99. The provider documentation supports the byte-stream stages and four-byte conversion, not this illustrative settlement rule.

Complete inputs and expected outputs

Use the strings exactly as shown, without quotation marks or trailing whitespace. The server seed is a literal UTF-8 string, not decoded hexadecimal.

Server seed: tg-rng-demo-server
Client seed: reader-example
Nonce: 7
HMAC block counter: 0
Message: reader-example:7:0

SHA-256 of the server seed:
af44fbb9b3710c9bb38aec8a7ac0b25140f34033376c7bb894626d34cfa5a026

HMAC-SHA256:
113eeb8d4b56244a2674b08b1332e4baf7552c927271d397b5963d868cd0b0bb

The first four decimal bytes are 17, 62, 235, 141. Reading them as a big-endian integer gives:

17 × 256³ + 62 × 256² + 235 × 256 + 141 = 289336205

The Stake conversion documentation describes combining four bytes as fractional terms:

u = 17/256 + 62/256² + 235/256³ + 141/256⁴
  = 289336205 / 4294967296
  = 0.06736633484251797

u × 10000 = 673.6633484251797
floor(u × 10000) / 100 = 6.73

Our rule rounds down when selecting the teaching bin; it does not simply round a number to two decimal places. Although u lies in [0, 1), it is on a finite grid with spacing 1 / 4294967296, not a continuous scale. That finite grid and the chosen bin boundaries are part of the mapping.

Browser code to reproduce the calculation

The following executable JavaScript calculates the commitment, HMAC, four-byte fraction and teaching roll, then reproduces the 10000-output sample. Run it in a modern browser console on an HTTPS page or localhost where Web Crypto is available.

(async () => {
  const encoder = new TextEncoder();
  const serverSeed = "tg-rng-demo-server";
  const clientSeed = "reader-example";
  const nonce = 7;
  const blockIndex = 0;

  function toHex(bytes) {
    return Array.from(bytes, byte =>
      byte.toString(16).padStart(2, "0")
    ).join("");
  }

  // Hash the literal UTF-8 server seed.
  const commitmentBytes = new Uint8Array(
    await crypto.subtle.digest(
      "SHA-256",
      encoder.encode(serverSeed)
    )
  );

  // The server seed is the HMAC key, not part of the message.
  const key = await crypto.subtle.importKey(
    "raw",
    encoder.encode(serverSeed),
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["sign"]
  );

  async function calculate(counter, block) {
    const message = `${clientSeed}:${counter}:${block}`;
    const bytes = new Uint8Array(
      await crypto.subtle.sign(
        "HMAC",
        key,
        encoder.encode(message)
      )
    );

    // Interpret the first four bytes in big-endian order.
    const first4 = Array.from(bytes.slice(0, 4));
    const integer =
      first4[0] * 256 ** 3 +
      first4[1] * 256 ** 2 +
      first4[2] * 256 +
      first4[3];

    const u = first4.reduce(
      (sum, byte, index) => sum + byte / 256 ** (index + 1),
      0
    );

    // Our teaching rule, not a verified casino settlement rule.
    const teachingRoll = Math.floor(u * 10000) / 100;

    return {
      message,
      hmac: toHex(bytes),
      first4,
      integer,
      u,
      scaled: u * 10000,
      teachingRoll
    };
  }

  console.log("Commitment hash:", toHex(commitmentBytes));
  console.log("Teaching example:", await calculate(nonce, blockIndex));

  // Ten bins: [0, 0.1), ..., [0.9, 1).
  const counts = Array(10).fill(0);
  for (let counter = 0; counter < 10000; counter++) {
    const { u } = await calculate(counter, 0);
    counts[Math.floor(u * 10)]++;
  }
  console.log("Counts for nonces 0–9999:", counts);

  // Enumerate all equally likely inputs in the 8-bit toy model.
  const remainderCounts = Array(6).fill(0);
  const acceptedCounts = Array(6).fill(0);
  for (let value = 0; value < 256; value++) {
    remainderCounts[value % 6]++;
    if (value < 252) acceptedCounts[value % 6]++;
  }
  console.log("Remainder counts:", remainderCounts);
  console.log("Counts after rejection:", acceptedCounts);
})();

The first calculation should return the full hash and HMAC shown above, bytes [17, 62, 235, 141], integer 289336205, fraction 0.06736633484251797 and teaching roll 6.73. The sample should return [977, 1012, 1037, 1027, 998, 1026, 973, 969, 1006, 975].

Changing the nonce to 8 changes the message to reader-example:8:0 and produces a different HMAC. You can perform that calculation because this teaching server seed is already known. It does not demonstrate prediction of future rounds with an unknown active server seed.

Why the same bytes can produce different game results

Bytes become a game result only after the game's mapping rules are applied. Matching the HMAC is insufficient if the mapping, byte-consumption order or payout parameters differ.

Game typeWhat result replay must reconstructWhy there is no universal formula
DiceDiscrete-bin selection and comparison with the player's predictionBin counts and rounding can differ
CrashA nonlinear conversion into a multiplierDistribution tails, house-edge parameters and limits matter
Card gameA card index or a deck shuffleIndependent index selection differs from drawing without replacement
MinesA set of unique positionsDuplicate handling and selection order must match
PlinkoA sequence of directions and the payout tableA path alone does not determine payment without the game settings

The Stake conversion documentation describes different ways to consume values, including selection without replacement. This does not mean all card games require unique cards, or all providers use the same HMAC protocol.

For an actual round, obtain the protocol version, rounding rules and payout parameters. Current Stake Dice settlement is not verified by this example: 6.73 belongs only to our stated teaching model.

What frequency tests can—and cannot—show

Frequency tests can reveal some mapping errors, but they cannot establish generator security. A simple remainder example illustrates bias, while our synthetic sample illustrates the limits of a nearly flat distribution.

Assume each 8-bit input from 0 through 255 is equally likely. Taking its remainder modulo 6 gives:

ResidueNumber of source values
043
143
243
343
442
542

The imbalance occurs because 256 is not divisible by 6. Rejecting values 252–255 and drawing again leaves 252 accepted values, with 42 for each residue. This equality depends on the stated uniform-source assumption and correct redrawing. It is a toy example, not an allegation about a provider.

A reproducible sample of 10000 outputs

Our sample produces similar counts across ten bins, but these are independently generated synthetic outputs, not casino measurements. It uses the same literal seeds, block 0, nonces 0 through 9999 inclusive and the first four bytes of each HMAC.

The bin index is floor(u * 10). The browser code above reproduces the entire sample.

Interval for uCount
[0.0, 0.1)977
[0.1, 0.2)1012
[0.2, 0.3)1037
[0.3, 0.4)1027
[0.4, 0.5)998
[0.5, 0.6)1026
[0.6, 0.7)973
[0.7, 0.8)969
[0.8, 0.9)1006
[0.9, 1.0)975

The equal-probability reference is 1000 outputs per bin. Counts close to that reference do not establish secret entropy, implementation security, real-game RTP, future frequencies or profit. A known deterministic sequence can also look well balanced.

How to verify a historical round

Save the commitment before betting, then check the revealed seed and replay the result separately after the protocol's documented disclosure process. A green tool status cannot replace either the timing evidence or provider-specific reconstruction.

  1. Save the commitment hash before the bet. Record when it was published and which server seed it identifies.
  2. Save the calculation details. Keep the protocol and version, client seed, nonce, byte-stream cursor or block counter, game settings and payout parameters.
  3. Wait for documented seed retirement and revelation. Select an actual historical round associated with that seed pair.
  4. Check the complete commitment hash. Use the provider's exact payload serialization and specified hash function.
  5. Replay the result. Reproduce byte generation, byte consumption, mapping and rounding for that game version.
  6. Compare the history and settlement. Check the outcome, prediction, stake, payout settings and recorded payment—not just one displayed number.

Gamba's documentation likewise distinguishes commitment verification from game-result verification. Its mnemonic-to-binary conversion remains provider-specific and is not interchangeable with our UTF-8 example.

Gamba documentation showing separate commitment verification and game-result verification

Two verification stages; Gamba's protocol must not be replaced with our teaching scheme.

The general Provably Fair verification guide provides a broader workflow. The technical requirement here is agreement across the full calculation chain, not the name of a button.

What to check when results disagree

First check the inputs and mapping rules: a mismatch is not, by itself, proof of fraud.

  • Confirm that both seeds belong to the selected historical round.
  • Check for a nonce that is off by one.
  • Check for a missing block counter or incorrect initial byte-stream cursor.
  • Distinguish literal UTF-8 strings from decoded hexadecimal bytes.
  • Match separators, field order, spaces and newlines exactly.
  • Use the correct protocol version, mapping and rounding.
  • Match payout parameters to the saved history.

If the discrepancy remains unexplained, preserve the inputs, history, calculations and screenshots, then ask the provider for an explanation. Do not send active secrets or a wallet recovery phrase.

What the ToolsGambling inline verifier checks

The inline verifier can check whether SHA-256 of a revealed literal seed matches the supplied commitment hash. Its game numbers use a different protocol: it does not reproduce our teaching roll of 6.73 or establish compatibility with current Stake or Spribe games.

To reproduce the local screenshot, fill in these four fields:

Interface labelValue to enter
SERVER SEED HASH (PRE-GAME)af44fbb9b3710c9bb38aec8a7ac0b25140f34033376c7bb894626d34cfa5a026
SERVER SEED (REVEALED)tg-rng-demo-server
CLIENT SEEDreader-example
NONCE7

Under HMAC ALGORITHM, select HMAC-SHA256, then click Verify Round. Do not append :0 to the client seed; this interface has no separate block-counter field.

Inline verifier fields filled with the synthetic commitment hash, server seed, client seed and nonce 7

Synthetic inputs on localhost, not a production page or casino wager.

The brand names beside the algorithm controls are interface hints, not verified compatibility claims. The interface's “Universal” wording and generic-formula explanation do not establish provider-specific result replay.

Why the tool displays 36.03 instead of 6.73

The tool signs reader-example:7, without the block suffix :0, and applies a different mapping. The commitment can therefore match while both the HMAC and displayed Dice result differ from our byte-stream example.

For the inline verifier:

Message:
reader-example:7

HMAC-SHA256:
164923c03bdddda005a0f12178e6b92855e8368c9b77a85d9db3ff63f4187df8

Its Dice calculation interprets the first ten hexadecimal characters as an integer, takes the remainder modulo 1000000 and divides by 10000. That produces 36.0251, which the interface displays as 36.03. It is not our first-four-byte, 10000-bin teaching rule.

Local verification result showing PASS, the full computed SHA-256 hash, inline HMAC and Dice display of 36.03

The commitment matches; the Dice display belongs only to this tool's calculation.

The visible PASS status and green badge establish only that the revealed string matches the supplied commitment. Full verification still requires evidence that the commitment was saved before the bet, followed by provider-specific result replay. The label VERIFIED FAIR does not expand that conclusion.

Commitment verification uses SHA-256 in both HMAC modes. Selecting HMAC-SHA512 changes the HMAC calculation, not the commitment hash function.

The full Provably Fair tool can also serve as a SHA-256 commitment-check helper for literal seeds. It uses HMAC-SHA512 over client:nonce and its own first-four-byte mapping, so it is neither a universal result-replay tool nor a reproduction of our block-0 example.

What verification does not prove about security or RTP

A matching commitment and replayed result support a particular calculation chain; they do not guarantee profit, withdrawals or security of the whole system. RTP is a separate question from byte generation.

Consider a conditional model with win probability 0.5, a gross winning payout of 1.98 times the stake—including the returned stake—and zero payout on a loss:

RTP = 0.5 × 1.98 = 99%
House edge = 1%

For total betting turnover of 1000:
expected gross payout = 990
expected net result = −10

These are expectations under the stated model, not an actual game's RTP or a session forecast. Output mapping and payout rules are distinct parts of the calculation.

A hash check alone does not establish that:

  • the server seed was selected without preselection;
  • its source had sufficient unpredictability;
  • inconvenient rounds were not aborted;
  • game and payout rules were implemented correctly;
  • withdrawals will be paid;
  • the system passed an independent security certification.

Independently choosing a client seed after the server commitment can reduce a specific preselection risk: tailoring the server seed to an already known client input. That conclusion depends on the protocol and its assumptions; it is not protection against every attack.

An active server seed can leak and still match its commitment. The matching hash remains valid even though secrecy has been lost. Rotating seeds after an arbitrary fixed number of bets does not eliminate all risks either. Secret generation, input-selection order, retirement and implementation all matter.

Finally, distrust “predictors” promising future outcomes from a public commitment hash alone. Our reproducible example works because the teaching secret is known; it does not recover an unknown seed from its hash. For real verification, use actual round history and the official protocol for the specific game version—without wallet recovery secrets or promises of profit.

FAQ

Frequently Asked Questions

Not in the HMAC protocol described here: you need the server seed to reconstruct the byte stream. Before revelation, save the commitment hash and round details. Other protocols may use a different verification process, so follow the specific game's documentation.

These are separate checks. Comparing the full hash establishes whether the revealed string matches the saved commitment. Comparing the roll checks result replay under the game's rules. Many different inputs can produce the same short displayed result.

Reconstruct every required block and the exact byte-stream cursor according to the game's protocol. Each HMAC-SHA256 block in this example contains 32 bytes, enough for eight non-overlapping four-byte values. The byte-stream cursor and HMAC block counter are not interchangeable.

No. It shows frequencies in selected intervals for particular inputs. It does not establish server-seed unpredictability, implementation security, real-game RTP or future frequencies.

No. The verification described here uses gaming seeds, hashes and round records without blockchain. A wallet recovery phrase is not a gaming seed: never enter it into a fairness tool or share it for verification.

Evgeniy Volkov

Verified Expert
Fullstack Developer

Fullstack developer with a background in mathematics. I build the calculators and game-style tools on ToolsGambling with Pixi.js and modern web tech, and every result uses transparent probability formulas you can verify yourself.

EducationMathematics
SpecializationiGaming
StatusActive

Was this article helpful?

Share Article

Free calculators and tools

Run the numbers before you bet. Our calculators use transparent formulas you can verify yourself.