Attempt to make a TRNG with Padauk PFS154

As we talked about in [A walk down the low-cost MCU-road], we landed on trying to make a random number generator with the rather low cost PadaukPFS154.

How can we create “true” randomness?

Before we dive more into the implementation we can look more into how true randomness can be achieved.

As stated earlier, we want to make:

A device that spits out true random numbers through a serial communication link

How we can achieve that is the most tricky part. As we talked more about in [Randomness: What is it? Why do we need it? How do we create it?], pseudo-randomness is rather easy to create, but true randomness requires more. The key here is to fetch entropy from something physical.

There exists several ways (and places) to fetch this entropy.

Maybe the most used, is to make a system that fetches noise from hardware-things such as zener diode breakdowns, an ADC, human keyboard timings, mouse movements, air turbulence etc.

Creating a TRNG

But we want to make this thing with as little (hopefully none) external requirements, and the PFS154 does not have an ADC-module, so randomness from or through an ADC is not possible.

Several people have implemented random number generator that uses 2 system clocks running at different speeds and when the slowest of them ticks, data is taken out from a counter connected to the other clock.

By doing so we can catch the clock noise and the jitter (that probably is close to 100% random).

The PFS154 has two clock systems that we think is completely independent of each other. Namely the IHRC and the ILRC, where the IHRC is the “fast one” running at typically 16 MHz, while the “slow one” / the ILRC runs at ~54 kHz.

We can pipe the two different clocks into separate timers, and take out some of the bits from the fast counter each time the “slow interrupt” triggers.

Implementation and some data

The T16 interrupt happens only at ~1.6 Hz to give the two clocks time to behave differently (the IHRC 8-bit counter wraps around ~39 000 times for every T16 interrupt).

But this means that we only get a full byte with data every 5 second! Resulting in a not so fast number generator…

First we tried fetching out 2 bits at each interrupt, giving us a whopping 1,25 bps, but the resulting data was clearly not random (see “round 1” results further down). So we reduced the speed by only fetching 1 bit of data from the counter every interrupt (data from this is called “round 2”).

All the data generated by this system in these two rounds are possible to download here and here.

After starting the different timers (including the bitbanged UART), the main thing left is the interrupt handler.

And the “number generator” looked like this:

if ( INTRQ & INTRQ_T16 )
{
    if ( random_number_index == 0 )
    {
        random_number = 0;
    }

    // take out one bit each time
    random_number |= ( ( (TM3CT & 0b00000010) >> 1) << random_number_index );

    if ( random_number_index >= 7 )
    {
        random_number_index = 0;
        new_number = 1;
    }
    else
    {
        random_number_index++;
    }

    // mark T16 interrupt request processed
    INTRQ &= ~INTRQ_T16;                            
}

For easy of use was the easypdk modified to dump incoming data to a binary-file:

// Changes added to easypdkprog.c line 714+:

FILE *fptr;

if ( dbgd>0 && !first_byte )
{

    dbgmsg[dbgd]=0;
    printf("%s", dbgmsg);

    fptr = fopen("serialdump.bin","ab");
    fwrite(&dbgmsg[0], 1, 1, fptr);
    fclose(fptr);
}

if ( first_byte )
{
    first_byte = false;
}

Is it random?

No.

If we start by looking at the distribution from the 2 different rounds:

You can quickly see that this is not uniformly distributed. And if we look at the development of this distribution we can also state that this lack several layers of “randomness”:

So by just looking quickly at the numbers we can state that this is not random.

Yes, the dataset is rather small (round 2 consist of only 9 189 912 bits), but still we can see that this is not going the “correct” way 🙂

For fun we ran this dataset through several statistical tests in dieharder. And the results did not surprise:

$ dieharder -g 201 -f serialdump_round2.bin -a
#=============================================================================#
#            dieharder version 3.31.1 Copyright 2003 Robert G. Brown          #
#=============================================================================#
   rng_name    |           filename             |rands/second|
 file_input_raw|           serialdump_round2.bin|  4.98e+06  |
#=============================================================================#
        test_name   |ntup| tsamples |psamples|  p-value |Assessment
#=============================================================================#
# The file file_input_raw was rewound 48 times
   diehard_birthdays|   0|       100|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 396 times
      diehard_operm5|   0|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 842 times
  diehard_rank_32x32|   0|     40000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 1051 times
    diehard_rank_6x8|   0|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 1142 times
   diehard_bitstream|   0|   2097152|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 1872 times
        diehard_opso|   0|   2097152|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 2359 times
        diehard_oqso|   0|   2097152|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 2587 times
         diehard_dna|   0|   2097152|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 2609 times
diehard_count_1s_str|   0|    256000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 3055 times
diehard_count_1s_byt|   0|    256000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 3063 times
 diehard_parking_lot|   0|     12000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 3069 times
    diehard_2dsphere|   2|      8000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 3073 times
    diehard_3dsphere|   3|      4000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 4199 times
     diehard_squeeze|   0|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 4199 times
        diehard_sums|   0|       100|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 4234 times
        diehard_runs|   0|    100000|     100|0.83092807|  PASSED
        diehard_runs|   0|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 4712 times
       diehard_craps|   0|    200000|     100|0.00000000|  FAILED
       diehard_craps|   0|    200000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 11677 times
 marsaglia_tsang_gcd|   0|  10000000|     100|0.00000000|  FAILED
 marsaglia_tsang_gcd|   0|  10000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 11711 times
         sts_monobit|   1|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 11746 times
            sts_runs|   2|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 11781 times
          sts_serial|   1|    100000|     100|0.00000000|  FAILED
          sts_serial|   2|    100000|     100|0.00000000|  FAILED
          sts_serial|   3|    100000|     100|0.00000000|  FAILED
          sts_serial|   3|    100000|     100|0.00000000|  FAILED
          sts_serial|   4|    100000|     100|0.00000000|  FAILED
          sts_serial|   4|    100000|     100|0.00000000|  FAILED
          sts_serial|   5|    100000|     100|0.00000000|  FAILED
          sts_serial|   5|    100000|     100|0.00000000|  FAILED
          sts_serial|   6|    100000|     100|0.00000000|  FAILED
          sts_serial|   6|    100000|     100|0.00000000|  FAILED
          sts_serial|   7|    100000|     100|0.00000000|  FAILED
          sts_serial|   7|    100000|     100|0.00000000|  FAILED
          sts_serial|   8|    100000|     100|0.00000000|  FAILED
          sts_serial|   8|    100000|     100|0.00000000|  FAILED
          sts_serial|   9|    100000|     100|0.00000000|  FAILED
          sts_serial|   9|    100000|     100|0.00000000|  FAILED
          sts_serial|  10|    100000|     100|0.00000000|  FAILED
          sts_serial|  10|    100000|     100|0.00000000|  FAILED
          sts_serial|  11|    100000|     100|0.00000000|  FAILED
          sts_serial|  11|    100000|     100|0.00000000|  FAILED
          sts_serial|  12|    100000|     100|0.00000000|  FAILED
          sts_serial|  12|    100000|     100|0.00000000|  FAILED
          sts_serial|  13|    100000|     100|0.00000000|  FAILED
          sts_serial|  13|    100000|     100|0.00000000|  FAILED
          sts_serial|  14|    100000|     100|0.00000000|  FAILED
          sts_serial|  14|    100000|     100|0.00000000|  FAILED
          sts_serial|  15|    100000|     100|0.00000000|  FAILED
          sts_serial|  15|    100000|     100|0.00000000|  FAILED
          sts_serial|  16|    100000|     100|0.00000000|  FAILED
          sts_serial|  16|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 11851 times
         rgb_bitdist|   1|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 11990 times
         rgb_bitdist|   2|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 12199 times
         rgb_bitdist|   3|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 12478 times
         rgb_bitdist|   4|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 12826 times
         rgb_bitdist|   5|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 13244 times
         rgb_bitdist|   6|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 13731 times
         rgb_bitdist|   7|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 14288 times
         rgb_bitdist|   8|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 14915 times
         rgb_bitdist|   9|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 15611 times
         rgb_bitdist|  10|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 16377 times
         rgb_bitdist|  11|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 17213 times
         rgb_bitdist|  12|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 17283 times
rgb_minimum_distance|   2|     10000|    1000|0.00000000|  FAILED
# The file file_input_raw was rewound 17387 times
rgb_minimum_distance|   3|     10000|    1000|0.00000000|  FAILED
# The file file_input_raw was rewound 17527 times
rgb_minimum_distance|   4|     10000|    1000|0.00000000|  FAILED
# The file file_input_raw was rewound 17701 times
rgb_minimum_distance|   5|     10000|    1000|0.00000000|  FAILED
# The file file_input_raw was rewound 17770 times
    rgb_permutations|   2|    100000|     100|0.00008615|   WEAK
# The file file_input_raw was rewound 17875 times
    rgb_permutations|   3|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 18014 times
    rgb_permutations|   4|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 18188 times
    rgb_permutations|   5|    100000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 18536 times
      rgb_lagged_sum|   0|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 19233 times
      rgb_lagged_sum|   1|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 20277 times
      rgb_lagged_sum|   2|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 21670 times
      rgb_lagged_sum|   3|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 23411 times
      rgb_lagged_sum|   4|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 25501 times
      rgb_lagged_sum|   5|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 27938 times
      rgb_lagged_sum|   6|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 30724 times
      rgb_lagged_sum|   7|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 33858 times
      rgb_lagged_sum|   8|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 37340 times
      rgb_lagged_sum|   9|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 41170 times
      rgb_lagged_sum|  10|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 45348 times
      rgb_lagged_sum|  11|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 49875 times
      rgb_lagged_sum|  12|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 54750 times
      rgb_lagged_sum|  13|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 59973 times
      rgb_lagged_sum|  14|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 65545 times
      rgb_lagged_sum|  15|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 71464 times
      rgb_lagged_sum|  16|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 77732 times
      rgb_lagged_sum|  17|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 84348 times
      rgb_lagged_sum|  18|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 91312 times
      rgb_lagged_sum|  19|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 98624 times
      rgb_lagged_sum|  20|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 106285 times
      rgb_lagged_sum|  21|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 114294 times
      rgb_lagged_sum|  22|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 122651 times
      rgb_lagged_sum|  23|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 131356 times
      rgb_lagged_sum|  24|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 140409 times
      rgb_lagged_sum|  25|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 149811 times
      rgb_lagged_sum|  26|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 159561 times
      rgb_lagged_sum|  27|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 169659 times
      rgb_lagged_sum|  28|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 180105 times
      rgb_lagged_sum|  29|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 190900 times
      rgb_lagged_sum|  30|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 202042 times
      rgb_lagged_sum|  31|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 213533 times
      rgb_lagged_sum|  32|   1000000|     100|0.00000000|  FAILED
# The file file_input_raw was rewound 213568 times
     rgb_kstest_test|   0|     10000|    1000|0.00000000|  FAILED
# The file file_input_raw was rewound 214103 times
     dab_bytedistrib|   0|  51200000|       1|0.00000000|  FAILED
# The file file_input_raw was rewound 214148 times
             dab_dct| 256|     50000|       1|0.00000000|  FAILED
Preparing to run test 207.  ntuple = 0
# The file file_input_raw was rewound 214541 times
        dab_filltree|  32|  15000000|       1|0.00000000|  FAILED
        dab_filltree|  32|  15000000|       1|0.00000000|  FAILED
Preparing to run test 208.  ntuple = 0
# The file file_input_raw was rewound 214626 times

The dieharder-tests probably reports several false negatives, because of the times it had to rewound the input data file. So if you actually are going to use a statistical test battery such as the dieharder tests, you need many GB of raw-data (compared to the ~1 MB here!)

Why is this not random?

We can clearly see that this data is not “random“.

But two independent hardware clocks should contain enough randomness and jitter which we can extract!?

Maybe they are not so independent as we thought?

Because when we calibrated one of the clocks the other clock started behaving differently, meaning that some sort of interaction happens. And maybe Padauk occasionally calibrates the ILRC with the the IHRC?

This is not a bad thing, but note that this behavior is not documented in the datasheet and makes it difficult to make a TRNG in this way on a PFS154.

Related Posts