From cryptographic randomness to provable fairness in random number generation
Using a cryptographically secure PRNG, seed management and entropy source design, uniform distribution testing, and passing independent RNG audit standards.
The Random Number Generator (RNG) is the cornerstone of fairness in gaming. Every slot machine spin, every card dealt, every dice outcome depends on the random numbers the RNG produces. If the RNG is biased, predictable, or open to manipulation, the fairness of the entire game cannot be guaranteed.
In regulated markets, the RNG must pass a rigorous audit by a third-party certification body (such as GLI or BMM) to confirm that it meets statistical standards of randomness. This is not merely a technical requirement; it is a regulatory threshold for launching a compliant game.
To understand the design trade-offs behind an RNG, you first need the concept of a threat model. The adversary we assume is not a player guessing at random, but an opponent with full reverse-engineering capability, able to collect large samples of outcomes over long periods, and possibly in possession of part of the program logic. Under that assumption, any design that is "secure because nobody can guess it" simply does not hold; the security of randomness must come from the mathematical structure itself, not from information asymmetry. This is why every security decision should rest on the premise that the attacker knows the algorithm and lacks only the keys and the internal state.
The random functions built into most programming languages are designed for speed and statistical uniformity, not for security. Take the common linear congruential generator (LCG): its next state is obtained from the current state with a single multiplication and addition, so an attacker who observes only a handful of consecutive outputs can solve for the internal parameters and predict the entire future sequence. Even the Mersenne Twister, whose statistical quality is excellent, requires only a few hundred consecutive outputs to be collected before the full internal state can be recovered and the whole sequence reconstructed.
The key point is this: passing statistical tests is not the same as being unpredictable. A sequence can perform flawlessly on the chi-square test and the runs test and still be entirely predictable. Statistical uniformity measures whether the output "looks random"; cryptographic security measures whether the future can be derived from past outputs. These are two independent properties. Gaming needs both, and neither can be omitted.
The RNG in a gaming product must use a cryptographically secure pseudorandom number generator (CSPRNG), not the standard random function provided by a general-purpose language. The key differences are:
In practice, we use the cryptographic random source provided by the operating system (on Linux, a system call such as getrandom();
on other platforms, the corresponding kernel randomness interface) as the underlying source of random numbers, so that every random number carries cryptographic-grade security.
Not implementing the algorithm ourselves, and pushing the trust boundary as far as possible toward the operating system and the hardware, is itself an important security design principle:
the kernel's randomness subsystem has undergone extensive public review and long-term field validation, and is far more reliable than any homegrown alternative.
A CSPRNG merely "stretches" a small amount of true randomness into a large volume of unpredictable bits; it does not create randomness on its own. Real randomness comes from an entropy source: physically unpredictable events. Modern operating systems continuously gather entropy from several independent channels: hardware random instructions on the CPU, timing jitter in interrupts, completion times of disk and network I/O, and a variety of microscopic timing differences that are hard to observe externally. These raw samples first enter the entropy pool and are hashed and mixed before being made available.
When assessing the quality of an entropy source, the metric that really matters is min-entropy (the entropy corresponding to the probability of the single most likely value), rather than Shannon entropy in the average sense. This is the conservative estimate, because an attacker guessing will always try the most likely value first. The relevant assessment methods are fully defined in NIST SP 800-90B, which covers IID testing and a family of min-entropy estimators.
In practice there are several easily overlooked entropy-source risks that deserve consideration at design time:
When we need a random integer in the range [0, max), the most intuitive approach is rand() % max.
This, however, introduces modulo bias: when the upper bound of the random number is not divisible by max,
some outcomes occur with slightly higher probability than others.
The mathematical cause of the bias is quite intuitive. Suppose the underlying random number takes values from 0 to N-1, that is N equally probable values,
and we want to map them onto 0 through max-1. If N is not divisible by max,
then dividing N by max leaves a remainder of r = N mod max "extra" values,
and those extra values are allocated to the first r outcomes. As a result, each of the first r outcomes corresponds to
⌊N/max⌋ + 1 raw values while the remaining outcomes correspond to only ⌊N/max⌋,
so the former occur with systematically higher probability.
An extreme miniature example makes this easiest to see: if the raw random number has only ten possible values, 0~9, and is mapped onto three outcomes,
then ten divided by three leaves a remainder of one, and that extra value falls to outcome 0. It therefore covers four raw values (0, 3, 6 and 9) for a probability of 4/10,
while outcomes 1 and 2 cover three values each, at 3/10 apiece.
This bias does not vanish as the number of samples grows; on the contrary, a large sample lets statistical tests pinpoint it precisely.
Although the bias is minuscule when max is far smaller than the range of the random number, in gaming any statistical non-uniformity can become an audit risk. More importantly, this bias is structural rather than random: it has a definite direction, it accumulates, and it can be reverse-engineered and exploited. We therefore use rejection sampling to eliminate the bias entirely:
threshold = MaxUint64 - (MaxUint64 % max)
do {
value = crypto_rand_uint64()
} while (value >= threshold)
result = value % max
The principle of this algorithm is to discard exactly those random numbers that would cause bias (the values falling in the incomplete final max-sized interval),
keeping only the ones that yield a perfectly uniform distribution. The rejection probability is extremely low (less than max / 2^64),
so the performance impact is negligible, while a mathematically perfect uniform distribution is guaranteed.
On a highly concurrent game server, managing the RNG seed is another critical security point. If two players' RNGs use the same seed, they will produce the same sequence of game outcomes.
To prevent seed collisions, we apply the following strategies:
A more complete way to think about it is to treat the seed as a sensitive asset with a lifecycle, rather than as a one-off initialization parameter. That lifecycle can be divided roughly into four stages, each with a principle that must be upheld:
There is one point here that is easily misunderstood: an unpredictable seed is not the same as a non-repeating seed. Under high concurrency, two instances can still collide with very small probability even when each seed comes from a good entropy source. So beyond quality assurance we also need a structural guarantee of uniqueness, which is precisely the purpose of folding an atomically incremented counter into the seed: it supplies no randomness, but it supplies a deterministic guarantee of non-repetition, complementing the unpredictability of the entropy source.
Compliance requires every RNG call to be traceable and replayable. In our system, every random number the RNG produces is written to the audit log:
This mechanism not only satisfies compliance requirements but is also an important QA testing tool: by injecting a predefined sequence of random numbers, precise deterministic tests can be run that cover every boundary condition.
The real value of an audit trail lies in traceability: when a dispute arises, can the complete causal chain of that moment be reconstructed? To achieve this, the recorded fields must be sufficient to reproduce the outcome independently after the fact, without depending on the execution environment of that moment. In practice, a record with full traceability must cover at least the following categories of information:
The guiding design principle is to record only what is needed to reproduce the outcome, and never the internal state. The audit log must be detailed enough to verify fairness, yet not so detailed that it leaks the internal state of the RNG. The line between those two requirements is exactly where audit trail design demands the most careful judgment.
Another critical dimension of RNG security is ensuring that the client cannot influence the random outcome. The design principle here is unambiguous: the client is an untrusted execution environment. All code running on a player's device can be decompiled, modified and replayed; all data arriving from the client should be treated as input an attacker can construct at will. The correct architecture is therefore not "verify that the client did not cheat" but "give the client no say whatsoever in the computation path of the outcome". The client is only responsible for presenting the outcome the server has already determined, and the playback order of animations and sound effects takes no part in any determination.
On that premise, our security audit framework covers the following checkpoints:
To pass GLI-19 (the technical standard for interactive gaming systems) or BMM certification, an RNG must satisfy the following statistical tests:
Understanding the role of these tests matters just as much. A statistical test is by nature a tool of refutation, not proof: it can tell you that a sequence clearly does not look random, but it can never prove that a sequence must be random. Passing the full suite only means "no evidence of deviation from the randomness hypothesis was found," not that the RNG is secure. The predictable generators mentioned earlier likewise sail through the vast majority of statistical tests.
Another common misconception in practice is treating test results as a binary pass or fail. At a significance level of α, even a perfectly healthy RNG is expected to produce roughly an α fraction of "unexpected failures" per hundred test runs. The correct way to read the results is therefore to look at the distribution of the p-values themselves: when the null hypothesis holds, p-values should be uniformly distributed between 0 and 1. If the p-values of a particular test persistently cluster in the extremes, that is the genuine warning sign; conversely, an isolated one-off failure is an expected random phenomenon, and overreacting to it only leads to misjudging a healthy system.
Our RNG implementation consistently passes all of the above tests in internal testing and regularly undergoes independent audits by third-party certification bodies, maintaining the highest standard of randomness throughout the product lifecycle.
In summary, our RNG security architecture is organized into three layers:
This multi-layer defensive design ensures that even if one layer fails, the others still provide protection, establishing a solid and reliable framework for guaranteeing fairness.
Layered defense works because the failure modes of the layers are mutually independent. If all three layers of protection rest on the same assumption, then what appears to be three layers is in reality still one: the moment that shared assumption is broken, all three fall at once. When designing, therefore, we deliberately aimed each layer at a different class of threat: the lower layer guards against mathematical predictability and distributional bias, the middle layer against state contamination at the implementation and concurrency level, and the upper layer against active manipulation attempts from outside. Only because their underlying assumptions do not overlap do the three constitute genuine defense in depth.
Finally, it is worth emphasizing that the quality of randomness is unobservable. A defective RNG shows nothing unusual in the outcome of any single round; it reveals itself only in the statistical distribution of a large sample. This means we cannot rely on after-the-fact manual inspection to catch problems, and must rely instead on the combination of three things: correctness at design time, automated and continuous statistical monitoring, and a complete audit trail. Fairness is not a property that holds forever after one pre-launch check; it is a commitment that must be verified continuously throughout the product lifecycle.