In plain words
- Before you bet, we lock in our secret for the coming rounds and show you a fingerprint of it.
- You can add randomness of your own, which we cannot predict.
- Each result follows from those two secrets and the round number, so it is fixed the moment you bet. Neither we nor you can change it afterwards.
- When our secret is revealed, you can recompute every result yourself and compare it with the fingerprint.
The Articles below describe the method exactly, for anyone who wants to check it.
Article 1. The method
- Before you play, we create a random server seed and show you its SHA-256 hash. The hash commits us to the seed without revealing it.
- Your client seed is mixed in. We choose a random one for you; you can replace it with your own at any time.
- A nonce counts your rounds with the current pair of seeds: 1 for the first round, 2 for the second, and so on.
- The random numbers of a round come only from the server seed, the client seed and the nonce. The game in your browser only animates the result our server sends back.
- When the pair of seeds is replaced, the old server seed is revealed, so that you can check every round played with it against the hash you were shown.
Article 2. Seeds and the round count
2.1 Your current server seed hash and nonce are shown below the bet panel in every game.
2.2 The pair of seeds is replaced automatically after 1,000 rounds, and whenever you set a new client seed. Setting a client seed therefore also reveals your previous server seed.
2.3 While signed in, you can read your current hash, client seed and nonce, together with the most recently revealed pair of seeds, at GET https://playnormandie.com/api/casino/fairness, and set a new client seed (letters and digits, up to 64 characters) with POST https://playnormandie.com/api/casino/fairness. Only the latest revealed seed is shown, so note it down if you want to check it later.
Article 3. From seeds to a result
3.1 Random numbers are read from a stream of HMAC-SHA256 blocks keyed with the server seed:
HMAC-SHA256(key = server_seed, message = "client_seed:nonce:cursor")
3.2 cursor starts at 0. Each 32-byte block gives eight numbers; when they are used up, the cursor goes up by one and the next block is computed.
3.3 Each number takes the next 4 bytes as an unsigned big-endian integer and divides it by 232, giving a value from 0 (included) to 1 (excluded).
3.4 A whole number in a range, such as a card or a roulette pocket, is min + floor(value × (max − min + 1)).
3.5 Each game turns these numbers into its outcome (a dice roll, a card, a mine position, a crash point) by a fixed rule. The same seeds and nonce always give the same result.
Article 4. Checking a round yourself
- Note the server seed hash, your client seed and the nonce of the round.
- After the seeds are replaced, take the revealed server seed and check that its SHA-256 hash matches the hash you were shown.
- Recompute the first random number of the round, for example in your browser's console:
const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(serverSeed),
{ name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
const block = new DataView(await crypto.subtle.sign('HMAC', key,
new TextEncoder().encode(`${clientSeed}:${nonce}:0`)));
const first = block.getUint32(0) / 2 ** 32; // next numbers: offsets 4, 8, … 28
Article 5. Return to player
The payout multipliers of every game are set so that, over a very large number of rounds, a game returns about 97% of the coins played (a house edge of 3%). This is a long-term average: in a single session you can win or lose far more. Coins won are virtual and have no cash value.