# Are my random numbers really “bad” or is that a statistical effect which “must” happen from time to time?

My software uses a TRNG (entropy source) which is part of an MCU.

A check of random numbers generated by my software, carried out by a testing authority revealed a problem, which I have to examine now:

They ran my random numbers against NIST Test suite: https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-22r1a.pdf

The Non-overlapping Template Matching Test failed.

It is looking for the number of occurrences of given bit sequences in a random string and compares those numbers with the expectation values. The test statistics $\chi^2$ is a measure, how well the observed number matches with the expected number of occurrences.

A big number of N*M bits is divided into N blocks of each M bits. The pattern has length m.

The mean value of hits is

$\mu = \frac{M-m+1}{2^m}$

The variance

$\sigma^2 = M (\frac{1}{2^m} - \frac{2 m - 1}{2^{2m}})$

The test statistic is

$\chi^2 (obs) = \sum_{i=1}^{N} \frac{(W_i-\mu)^2}{\sigma^2}$

with i iterating over all N blocks and $W_i$ denoting the number of matches in block i.

The p-value is

$p = 1-\Gamma_{inc}(\frac{N}{2}, \frac{\chi^2 (obs)}{2})$ where $\Gamma_{inc}$ is the incomplete gamma function.

If the computed P-value is < 0.01, then conclude that the sequence is non-random.

I implemented this test on my own and got with a test run of 80,000 random bytes:

N=8, M=80000

A good pattern would be:

expected occurrences of 1110000 in random binary of length 80000:624
W[0]:657
W[1]:633
W[2]:620
W[3]:608
W[4]:659
W[5]:610
W[6]:607
W[7]:628
OK: P=0.697173010616


Most patterns are good, but some are very bad, like:

expected occurrences of 1110110 in random binary of length 80000:624
W[0]:554
W[1]:618
W[2]:715
W[3]:648
W[4]:602
W[5]:619
W[6]:683
W[7]:611
*****NOT OK: P=0.000101720454752


or

expected occurrences of 11000100 in random binary of length 80000:312
W[0]:310
W[1]:352
W[2]:297
W[3]:325
W[4]:332
W[5]:376
W[6]:327
W[7]:318
*****NOT OK: P=0.00404698900088


Now the question:

As I interpret the P value, it is the probability for getting the observed test statistics given the sequence is random. So P=0.000101720454752 would be extremely unlikely, so the test fails.

Most interestingly, when I increase the block size by a factor of 10 (800,000 random bytes)

N=8, M=800000

I get only good results on all patterns.

What does it really mean now? Are my random numbers really "bad" or is that a statistical effect which "must" happen from time to time? Anyway the test fails...

And why is it OK for the ten-fold data-set?

I would appreciate any comments. Thanks.

• Have you tried your data against more reliable tests like DieHard(er)? – Paul Uszak Mar 28 '18 at 20:39
• What about providing a link to some output of the presumed faulty generator, including at least one that fails the test? – fgrieu Mar 29 '18 at 10:15

One of the failing test discussed in the question was coded for the purpose. It could be useful to validate that code using a known-good pseudo-random source (the output of SHA-256 for incremental values qualifies). If it failed too often, the code would need a fix! The test's definition is complex, for example in 2.7.4 (2) defining how to count the number of occurrences of the pattern searched, and an error could have crept.

But I fail to imagine an error giving what's observed, and (after fixing an issue about the definition of the incomplete gamma function that had no influence for the reference test case, and a typo of mine in one count) I confirm that the question's P-values match the question's counts.

With these results (especially the second test case, with a P-value < 1/9830), at least one of the following must hold:

• the generator's output is quite distinguishable from random (my guess);
• that's a serious case of bad luck (how bad depends strongly on among how many tests the failing cases shown have been picked, see below);
• the question's counts are somewhat wrong in a strange, irregular way;
• the math in the reference is seriously bad (but I guess that would be known).

Importantly: it appears that the test is run with many patterns. In that case, the P-value threshold considered a failure should be lowered (like, divided by the number of tests); or best, the P-values should be aggregated. For aggregation, we could use Fisher's method. In theory that requires the tests to be considered independent, which would be the case if a sample RNG output is never tested with multiple patterns. Practitioners occasionally are not so careful, but that seldom is a game-changer.

Note: The reference only skims at the issue of choosing P-value thresholds (noted $\alpha$), and does not tell how to decide if the overall result of multiple tests is pass or fail. It even makes highly misleading statements on how to interpret P-values, like the second of these three sentences at end of section 1.1.5 (emphasis mine):

An $\alpha$ of 0.001 indicates that one would expect one sequence in 1000 sequences to be rejected by the test if the sequence was random. For a P-value ≥ 0.001, a sequence would be considered to be random with a confidence of 99.9%. For a P-value < 0.001, a sequence would be considered to be nonrandom with a confidence of 99.9%.

The question rightly observes

Most interestingly, when I increase the block size by a factor of 10 (800,000 random bytes: N=8, M=800000) I get only good results on all patterns.

Especially if the generator tested is an unconditioned TRNG (see below), that would match a failure mode where the generator locally has repeating patterns, but these evolve over time.

If the source of the data tested is an unconditioned TRNG, and unless the supplier of that thing guarantees in a contract with severe penalties that no conditioning is necessary to pass any kind of test (which would be a foolish move), then failing a RNG test should be considered normal. Correspondingly, submitting this output for validation by a lab should be considered a procedural mistake: these tests are intended to test if a RNG is distinguishable from random, and unconditioned TRNGs often are!

On the other hand, if what's tested is the output of a PRNG seeded with the TRNG (as would be typical for a black-box test), and the test fails, that's indicative of (both) an extremely poor/defective PRNG and a failure of the TRNG.

Unconditioned TRNGs typically have defects, often subtle, dependent on manufacturing variations and environmental conditions. The design engineer has to factor that in. The output of an unconditioned TRNG should be used (exclusively when in operational mode):

• to monitor failure of the unconditioned TRNG; the monitoring's goal is ensure that the source delivers some entropy, above some designed-in limit, without raising false alarms.
• and to feed a PRNG, which results are used (not the TRNG's unconditioned output).

Further, if that PRNG is not a CSPRNG (which is typical of hardware PRNGs, often a variation around LFSR), for some critical usages the output of the PRNG should be the input of a good CSPRNG (it is often considered OK to directly use the hardware PRNG for more mundane things, like massive DPA countermeasures).

It is easy to turn a passable TRNG into a good RNG that passes all standard test. What's hard is to make monitoring that demonstrably detect failure on the field and acts accordingly, but does not falsely detect failure under normal use. True failure could be because attackers do all sort of nasty things: under/overvoltage, heat or evaporation of a liquefied gas to reduce the entropy rate of the RNG source, or superimposed clock/glitches on the power supply (and more) in an attempt to control the TRNG source and make it near deterministic. Sometime the adversary is only Murphy's law (perhaps in the form of poor contacts, long wires, unclean power supply, insufficient decoupling, manufacturing defect) but succeeds just as well.

Note: I heard a tale about a then inexperienced hardware manufacturer proposing an IC with module initially specified as a directly usable TRNG that always work; then found to sometime fail a test; and hastily replaced by a conditioned TRNG, with no access to the unconditioned TRNG specified. That reportedly made it difficult to properly monitor the TRNG: the only sound option would have been to break the conditioning, but the tale ends without clear indication that this was achieved and deployed.

How many patterns are you testing? If you are looking for many different patterns. It is expected that at least one of them would be more common. Normally we want the p value to be small to show an affect and use multi hypothesis correction of some sort. However here that would be cheating in our favour and therefor this is not a good solution. But increasing the amount of data as you did is. If the top patterns only show up in small samples but in a large sample you don't see anything problematic. I would trust the large sample. If a certain pattern really did appear slightly more often the larger sample would have only made the p value more extreme. Blame multi hypothesis testing.

• I agree, also, what fraction of patterns fail, and how badly? Can you check all the 9-bit patterns in a reasonable timeframe? – kodlu Mar 29 '18 at 0:32