0

I've been using the Intel-provided RNG feature for some time, to provide myself with some randomness by means of a C++/CLI program I wrote myself.

However, after some time, something struck me as particularly suspicious. Among other uses, I ask for a random number between 1 and 4 and wrote the result down on paper each time. Here are the results :

2, 3, 3, 2, 1, 3, 4, 2, 3, 2, 3, 1, 3, 2, 3, 1, 2, 4, 2, 2, 1, 2, 1, 3, 1, 3, 3, 3, 3.

Number of 1s : 6 Number of 2s : 9 Number of 3s : 12 Number of 4s : 2 Total : 29

I'm actually wondering if there's a problem either with Intel's RNG, my algorithm, methodology or something else maybe ? Or do you consider the bias not to be significant enough yet ?

I'm using Windows 10 Pro, my CPU is an Intel Core i7-4710MQ. Compiled with VS2017.

Methodology :

  1. Start a Powershell command prompt
  2. Load my assembly with Add-Type -Path <mydll>
  3. Invoke [rdrw.Random]::Next(4)
  4. Add one to the result

A detail that may be of importance : I don't ask for that number very often, so there's some time between draws and it usually comes when the RNG hasn't been used for some time (one hour at least).

And yes it's a lazy algorithm, I didn't want to bother myself with exceptions.

Algorithm follows :

#include <immintrin.h>

namespace rdrw {

#pragma managed(push,off)
    unsigned long long getRdRand() {
        unsigned long long val = 0;

        while (!_rdrand64_step(&val));
        return val;
    }
#pragma managed(pop)

    public ref class Random abstract sealed
    {
    public:
        // Returns a random 64 bit unsigned integer
        static unsigned long long Next() {
            return getRdRand();
        }

        // Return a random unsigned integer between 0 and max-1 (inclusive)
        static unsigned long long Next(unsigned long long max) {
            unsigned long long nb = max - 1;
            unsigned long long mask = 1;
            unsigned long long draw = 0;

            if (max <= 1)
                return 0;

            // Create a bitmask that's at least as big as the biggest acceptable value
            while ((nb&mask) != nb)
            {
                mask <<= 1;
                mask |= 1;
            }

            do
            {
                // Throw unnecessary bits
                draw = Next() & mask;
            } while (draw>nb);
            return draw;
        }

        // return a random unsigned integer between min and max-1 inclusive
        static unsigned long long Next(unsigned long long min, unsigned long long max) {

            if (max == min)
                return min;
            if (max < min)
                return 0;
            unsigned long long diff = max - min;
            return Next(diff) + min;
        }
    };
}

Thanks for your insights !

NovHak
  • 101
  • 1
  • 4
    Pretty far from significant, indeed. Try at least a hundred thousand draws and if the result is still biased to a specific value then there are clear issues. Full random tests from NIST require about 7 GB (max, to run all the tests without repeating random values for the tests). Needless to say, you want to use some kind of table in your application instead of a piece of paper. – Maarten Bodewes Nov 11 '18 at 22:02
  • @MaartenBodewes : I hear you, you must be right and I'll continue assuming there's no bias for now ! – NovHak Nov 12 '18 at 00:51
  • However, that got me the idea of something vicious. Let's imagine the RNG is perfectly honest except for the first generation after boot (or after a given amount of RNG idle time) that would be predictable, for example the result of a function taking system time as input. That would pass all those 7GB statistical tests, but still, that first draw would be predictable. Of course that would not be much, but who knows if it could not be exploited in some way ? – NovHak Nov 12 '18 at 01:01
  • There is no way to find out if the RNG is honest. You could be looking at a stream cipher with a known key, for all you know. You can only test if the RNG is behaving itself within the test set (and then conclude that it probably will for the other values as well). This is also why `/dev/urandom` only uses the CPU as seed on Linux. – Maarten Bodewes Nov 12 '18 at 02:30
  • Another note: if you extract out the random bit generator then you may be able to test the code *without* the randomness. Then the code is correct *assuming* that the random bit source is indeed random. – Maarten Bodewes Nov 12 '18 at 02:42
  • @MaartenBodewes : Extracting the RNG is beyond my current knowledge, especially considering it's built inside the CPU itself. The Intel Secure Key technology is implemented via a new CPU instruction set, most notably including RDRAND and RDSEED opcodes (my CPU is too old and doesn't implement RDSEED however if I'm correct, didn't try though) – NovHak Nov 12 '18 at 03:47
  • I mean: you could have the RNG as parameter and test the function with known bits. Then you can test the function like any other function (the function actually implements something called the Simple Discard Method by NIST, by the way). – Maarten Bodewes Nov 12 '18 at 13:21
  • @MaartenBodewes : Ah you mean test _my RNG_. Well I debugged it a few times already I didn't see anything wrong, but it's not very complicated after all and I thought if there's something wrong, maybe new eyes would notice. – NovHak Nov 12 '18 at 16:15

0 Answers0