# Raspberry Pi Pico Random Number Generator **This fork includes an enhanced RNG to transform a Pico onto a true hardware RNG.** A basic random number generator that generates numbers from enviromental noise with the onboard DAC of the Raspberry Pi Pico. The project uses the Raspberry Pi Pico USB dev_lowlevel as a starting point. The Pico RNG is not meant to be FIPS 140-2 compliant as a stand-alone device by any means. However it does supply the Linux Kernel with random bits that is used with the appropriate entropy to achieve FIPS 140-2 compliant random numbers. Maybe one day the next gen Pico's will include an onboard crypto module. ## Project Goals * Raspberry Pi Pico firmware generates random numbers as a USB Endpoint. * Linux Kernel Module (aka driver) provides random numbers to the Kernel. * Driver can transmit random numbers on demand to the system and/or user processes via a character device. ## Prerequisites * Raspberry Pi Pico development environment. See [Raspberry Pi Pico Getting Started Documentation](https://www.raspberrypi.org/documentation/pico/getting-started/) * Linux Kernel development headers ## Building The entire project uses CMake to keep with Rasberry Pi Pico's development environment and project setup instructions. ```bash # Create build directory mkdir build # Change to the build directory cd build # Run cmake cmake .. # Run make make ``` ## Install The driver can be installed from the build directory using the traditional insmod command. ```bash # Assumes CWD is 'build/' # debug will enable debug log level # timeout will set the usb endpoint timeout. Currently defaults to 100 msecs sudo insmod driver/pico_rng.ko [debug=1] [timeout=] ``` The Pico firmware is installed thorugh the normal process as outlined in the Raspberry Pi Pico Development Documentation. * Unplug the Pico from the host. * Plug the Pico into the host while holding the 'boot' button. * Mount the Pico ```sudo mount /dev/sdb1 /mnt```. Note /dev/sdb1 could be different you. Use ```sudo dmesg``` to find out what device the Pico shows up as on your system. * Copy the uf2 file to the Pico ```sudo cp firmware/pico_rng.uf2 /mnt```. * Umount the pico ```sudo umount /mnt```. ## Testing You can test Pico RNG firmware with the [pico_rng_test.py](firmware/pico_rng_test.py) script. ```bash # Running with --performance will measure the devices' KB/s. # if the kernel module has been installed, then the test tool will use /dev/pico_rng otherwise python's libusb implementation will be used. sudo firmware/pico_rng_test.py [--performance] [--size] [--endless] ``` You can also test the Kernel's random number pool that contains random numbers from the Pico ![Pico Random Numbers](pico-rng.gif) ## Analysis A set of random samples can be analyzed with the [pico_rng_analyze.py](firmware/pico_rng_analyze.py) script. For generating a set of samples, the following code can be executed: ```bash $ python3 firmware/pico_rng_test.py --size 1073741824 > sample.rng ``` will produce a 1 GB file containing random bytes. Then ```bash $ python3 firmware/pico_rng_analyze.py sample.rng ``` will produce two figures: ![fig1](https://user-images.githubusercontent.com/55573252/185908242-9fac9e7c-bfd2-440b-bd1d-045b571ed010.png) Fig. 1 depicts the distribution of the bytes, from `0` to `255`. Ideally, it must be a uniform distribution, with mean equal to `127.5`. ![fig2](https://user-images.githubusercontent.com/55573252/185908272-42973878-7c4f-45ec-8492-6878f8a0bfa6.png) Fig. 2 depicts the distribution of chi square tests and excess percentages. Ideally, the distribution of chi square tests must follow a chi square distribution with mean at `255`. Excess percentage distribution must follow a uniform distribution. Pico-rng is also tested with `ent`, `rngtest`, `sp800-90b` and `dieharder` tests. These are the results obtained with pico-rng: ### Ent ``` $ ent sample.rng Entropy = 7.999999 bits per byte. Optimum compression would reduce the size of this 1073741824 byte file by 0 percent. Chi square distribution for 1073741824 samples is 839.56, and randomly would exceed this value less than 0.01 percent of the times. Arithmetic mean value of data bytes is 127.4993 (127.5 = random). Monte Carlo value for Pi is 3.141625006 (error 0.00 percent). Serial correlation coefficient is 0.000006 (totally uncorrelated = 0.0). ``` - Entropy test should produce an ideal result of `8`, as each byte has `8` bits. - Optimum compression test should produce an ideal result of 0 percent. - Chi square should be called with different sample tests and produce a chi square distribution. - Arithmetic mean should produce an ideal result of `127.5`. - Pi test should be equal to pi number. - Serial correlation should be `0`. These results show that pico-rng is pretty random. ### FIPS 140-2 ```bash $ cat sample.rng | rngtest rngtest 2-unofficial-mt.14 Copyright (c) 2004 by Henrique de Moraes Holschuh This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. rngtest: starting FIPS tests... rngtest: entropy source exhausted! rngtest: bits received from input: 8589934592 rngtest: FIPS 140-2 successes: 429141 rngtest: FIPS 140-2 failures: 355 rngtest: FIPS 140-2(2001-10-10) Monobit: 42 rngtest: FIPS 140-2(2001-10-10) Poker: 41 rngtest: FIPS 140-2(2001-10-10) Runs: 138 rngtest: FIPS 140-2(2001-10-10) Long run: 136 rngtest: FIPS 140-2(2001-10-10) Continuous run: 0 rngtest: input channel speed: (min=9661835.749; avg=23078154805.083; max=0.000)bits/s rngtest: FIPS tests speed: (min=5.699; avg=239.429; max=261.281)Mibits/s rngtest: Program run time: 34612429 microseconds ``` In these results, we have a total number of trials `429496 (429141+355)`. - The acceptable result of Monobit test is `1` failed trial for every `9662` trials. For `429496` trials, the number of failed trials should be less than `429496/9662=44.45`. - The acceptable result of Poker test is `1` failed trial for every `10078` trials. For `429496` trials, the number of failed trials should be less than `42.62`. ### SP800-90B SP800-90b is the superseded version of FIPS 140-2 (`rngtest`). Can be obtained from here [https://github.com/usnistgov/SP800-90B_EntropyAssessment](https://github.com/usnistgov/SP800-90B_EntropyAssessment). ```bash $ ./ea-iid sample.rng Calculating baseline statistics... H_original: 7.995587 H_bitstring: 0.999863 min(H_original, 8 X H_bitstring): 7.995587 ** Passed chi square tests ** Passed length of longest repeated substring test ** Passed IID permutation tests ``` ### Diehard It is a set of different tests. ```bash $ dieharder -a -g 201 -k 2 -Y 1 -m 2 -f sample.rng #=============================================================================# # dieharder version 3.31.1 Copyright 2003 Robert G. Brown # #=============================================================================# rng_name | filename |rands/second| file_input_raw| batch1.rng| 3.57e+07 | #=============================================================================# test_name |ntup| tsamples |psamples| p-value |Assessment #=============================================================================# diehard_birthdays| 0| 100| 200|0.17739498| PASSED diehard_operm5| 0| 1000000| 200|0.97387049| PASSED # The file file_input_raw was rewound 1 times diehard_rank_32x32| 0| 40000| 200|0.37394022| PASSED # The file file_input_raw was rewound 2 times diehard_rank_6x8| 0| 100000| 200|0.09038266| PASSED # The file file_input_raw was rewound 2 times diehard_bitstream| 0| 2097152| 200|0.71785604| PASSED # The file file_input_raw was rewound 4 times diehard_opso| 0| 2097152| 200|0.37146068| PASSED # The file file_input_raw was rewound 5 times diehard_oqso| 0| 2097152| 200|0.35763562| PASSED # The file file_input_raw was rewound 5 times diehard_dna| 0| 2097152| 200|0.01889729| PASSED # The file file_input_raw was rewound 5 times diehard_count_1s_str| 0| 256000| 200|0.63258197| PASSED # The file file_input_raw was rewound 6 times diehard_count_1s_byt| 0| 256000| 200|0.65359815| PASSED # The file file_input_raw was rewound 6 times diehard_parking_lot| 0| 12000| 200|0.83176205| PASSED # The file file_input_raw was rewound 6 times diehard_2dsphere| 2| 8000| 200|0.64543002| PASSED # The file file_input_raw was rewound 6 times diehard_3dsphere| 3| 4000| 200|0.01258942| PASSED # The file file_input_raw was rewound 8 times diehard_squeeze| 0| 100000| 200|0.67578527| PASSED # The file file_input_raw was rewound 8 times diehard_sums| 0| 100| 200|0.23981668| PASSED # The file file_input_raw was rewound 8 times diehard_runs| 0| 100000| 200|0.77427455| PASSED diehard_runs| 0| 100000| 200|0.08390099| PASSED # The file file_input_raw was rewound 9 times diehard_craps| 0| 200000| 200|0.59348655| PASSED diehard_craps| 0| 200000| 200|0.23828837| PASSED # The file file_input_raw was rewound 24 times marsaglia_tsang_gcd| 0| 10000000| 200|0.15517856| PASSED marsaglia_tsang_gcd| 0| 10000000| 200|0.00447239| WEAK # The file file_input_raw was rewound 31 times marsaglia_tsang_gcd| 0| 10000000| 300|0.02554040| PASSED marsaglia_tsang_gcd| 0| 10000000| 300|0.00100578| WEAK # The file file_input_raw was rewound 39 times marsaglia_tsang_gcd| 0| 10000000| 400|0.00299305| WEAK marsaglia_tsang_gcd| 0| 10000000| 400|0.00008767| WEAK # The file file_input_raw was rewound 46 times marsaglia_tsang_gcd| 0| 10000000| 500|0.00201489| WEAK marsaglia_tsang_gcd| 0| 10000000| 500|0.00004067| WEAK # The file file_input_raw was rewound 54 times marsaglia_tsang_gcd| 0| 10000000| 600|0.00028460| WEAK marsaglia_tsang_gcd| 0| 10000000| 600|0.00000323| WEAK # The file file_input_raw was rewound 61 times marsaglia_tsang_gcd| 0| 10000000| 700|0.00004334| WEAK marsaglia_tsang_gcd| 0| 10000000| 700|0.00000055| FAILED # The file file_input_raw was rewound 61 times sts_monobit| 1| 100000| 200|0.11868265| PASSED # The file file_input_raw was rewound 61 times sts_runs| 2| 100000| 200|0.55674493| PASSED # The file file_input_raw was rewound 61 times sts_serial| 1| 100000| 200|0.99669244| WEAK sts_serial| 2| 100000| 200|0.46102720| PASSED sts_serial| 3| 100000| 200|0.44562932| PASSED sts_serial| 3| 100000| 200|0.84230024| PASSED sts_serial| 4| 100000| 200|0.98548763| PASSED sts_serial| 4| 100000| 200|0.57495245| PASSED sts_serial| 5| 100000| 200|0.77085975| PASSED sts_serial| 5| 100000| 200|0.56476358| PASSED sts_serial| 6| 100000| 200|0.45650075| PASSED sts_serial| 6| 100000| 200|0.05289356| PASSED sts_serial| 7| 100000| 200|0.27201114| PASSED sts_serial| 7| 100000| 200|0.76400695| PASSED sts_serial| 8| 100000| 200|0.62348338| PASSED sts_serial| 8| 100000| 200|0.57277685| PASSED sts_serial| 9| 100000| 200|0.38071419| PASSED sts_serial| 9| 100000| 200|0.07625829| PASSED sts_serial| 10| 100000| 200|0.47989859| PASSED sts_serial| 10| 100000| 200|0.11112817| PASSED sts_serial| 11| 100000| 200|0.92833035| PASSED sts_serial| 11| 100000| 200|0.69376394| PASSED sts_serial| 12| 100000| 200|0.84887524| PASSED sts_serial| 12| 100000| 200|0.97617601| PASSED sts_serial| 13| 100000| 200|0.65397230| PASSED sts_serial| 13| 100000| 200|0.56436970| PASSED sts_serial| 14| 100000| 200|0.51868029| PASSED sts_serial| 14| 100000| 200|0.92953669| PASSED sts_serial| 15| 100000| 200|0.85158213| PASSED sts_serial| 15| 100000| 200|0.93993540| PASSED sts_serial| 16| 100000| 200|0.35998600| PASSED sts_serial| 16| 100000| 200|0.20807684| PASSED # The file file_input_raw was rewound 61 times sts_serial| 1| 100000| 300|0.19573096| PASSED sts_serial| 2| 100000| 300|0.85406665| PASSED sts_serial| 3| 100000| 300|0.06590447| PASSED sts_serial| 3| 100000| 300|0.45707402| PASSED sts_serial| 4| 100000| 300|0.44442263| PASSED sts_serial| 4| 100000| 300|0.52775404| PASSED sts_serial| 5| 100000| 300|0.40217529| PASSED sts_serial| 5| 100000| 300|0.06010705| PASSED sts_serial| 6| 100000| 300|0.08040223| PASSED sts_serial| 6| 100000| 300|0.00547743| PASSED sts_serial| 7| 100000| 300|0.24575210| PASSED sts_serial| 7| 100000| 300|0.28977315| PASSED sts_serial| 8| 100000| 300|0.36868885| PASSED sts_serial| 8| 100000| 300|0.49522777| PASSED sts_serial| 9| 100000| 300|0.40957917| PASSED sts_serial| 9| 100000| 300|0.09110499| PASSED sts_serial| 10| 100000| 300|0.77559781| PASSED sts_serial| 10| 100000| 300|0.19716611| PASSED sts_serial| 11| 100000| 300|0.80459107| PASSED sts_serial| 11| 100000| 300|0.44572782| PASSED sts_serial| 12| 100000| 300|0.20184427| PASSED sts_serial| 12| 100000| 300|0.60780140| PASSED sts_serial| 13| 100000| 300|0.76273905| PASSED sts_serial| 13| 100000| 300|0.60473178| PASSED sts_serial| 14| 100000| 300|0.24687487| PASSED sts_serial| 14| 100000| 300|0.66722555| PASSED sts_serial| 15| 100000| 300|0.85162176| PASSED sts_serial| 15| 100000| 300|0.81632309| PASSED sts_serial| 16| 100000| 300|0.72398028| PASSED sts_serial| 16| 100000| 300|0.26061444| PASSED # The file file_input_raw was rewound 61 times rgb_bitdist| 1| 100000| 200|0.80303319| PASSED # The file file_input_raw was rewound 62 times rgb_bitdist| 2| 100000| 200|0.44511426| PASSED # The file file_input_raw was rewound 62 times rgb_bitdist| 3| 100000| 200|0.15306427| PASSED # The file file_input_raw was rewound 63 times rgb_bitdist| 4| 100000| 200|0.52424083| PASSED # The file file_input_raw was rewound 64 times rgb_bitdist| 5| 100000| 200|0.62602197| PASSED # The file file_input_raw was rewound 64 times rgb_bitdist| 6| 100000| 200|0.27582205| PASSED # The file file_input_raw was rewound 65 times rgb_bitdist| 7| 100000| 200|0.83928387| PASSED # The file file_input_raw was rewound 67 times rgb_bitdist| 8| 100000| 200|0.78920891| PASSED # The file file_input_raw was rewound 68 times rgb_bitdist| 9| 100000| 200|0.10426661| PASSED # The file file_input_raw was rewound 69 times rgb_bitdist| 10| 100000| 200|0.56345552| PASSED # The file file_input_raw was rewound 71 times rgb_bitdist| 11| 100000| 200|0.67837353| PASSED # The file file_input_raw was rewound 73 times rgb_bitdist| 12| 100000| 200|0.77285406| PASSED # The file file_input_raw was rewound 73 times rgb_minimum_distance| 2| 10000| 2000|0.99326519| PASSED # The file file_input_raw was rewound 73 times rgb_minimum_distance| 3| 10000| 2000|0.89490386| PASSED # The file file_input_raw was rewound 74 times rgb_minimum_distance| 4| 10000| 2000|0.63482997| PASSED # The file file_input_raw was rewound 74 times rgb_minimum_distance| 5| 10000| 2000|0.04956198| PASSED # The file file_input_raw was rewound 74 times rgb_permutations| 2| 100000| 200|0.59389551| PASSED # The file file_input_raw was rewound 74 times rgb_permutations| 3| 100000| 200|0.84473217| PASSED # The file file_input_raw was rewound 75 times rgb_permutations| 4| 100000| 200|0.67098621| PASSED # The file file_input_raw was rewound 75 times rgb_permutations| 5| 100000| 200|0.71477222| PASSED # The file file_input_raw was rewound 76 times rgb_lagged_sum| 0| 1000000| 200|0.20844392| PASSED # The file file_input_raw was rewound 77 times rgb_lagged_sum| 1| 1000000| 200|0.52704060| PASSED # The file file_input_raw was rewound 79 times rgb_lagged_sum| 2| 1000000| 200|0.68631913| PASSED # The file file_input_raw was rewound 82 times rgb_lagged_sum| 3| 1000000| 200|0.04971017| PASSED # The file file_input_raw was rewound 86 times rgb_lagged_sum| 4| 1000000| 200|0.11342937| PASSED # The file file_input_raw was rewound 91 times rgb_lagged_sum| 5| 1000000| 200|0.08126696| PASSED # The file file_input_raw was rewound 96 times rgb_lagged_sum| 6| 1000000| 200|0.20866831| PASSED # The file file_input_raw was rewound 102 times rgb_lagged_sum| 7| 1000000| 200|0.52239049| PASSED # The file file_input_raw was rewound 109 times rgb_lagged_sum| 8| 1000000| 200|0.68232225| PASSED # The file file_input_raw was rewound 116 times rgb_lagged_sum| 9| 1000000| 200|0.13755031| PASSED # The file file_input_raw was rewound 124 times rgb_lagged_sum| 10| 1000000| 200|0.82473876| PASSED # The file file_input_raw was rewound 133 times rgb_lagged_sum| 11| 1000000| 200|0.10861154| PASSED # The file file_input_raw was rewound 143 times rgb_lagged_sum| 12| 1000000| 200|0.10432254| PASSED # The file file_input_raw was rewound 153 times rgb_lagged_sum| 13| 1000000| 200|0.18082599| PASSED # The file file_input_raw was rewound 164 times rgb_lagged_sum| 14| 1000000| 200|0.13220862| PASSED # The file file_input_raw was rewound 176 times rgb_lagged_sum| 15| 1000000| 200|0.08158678| PASSED # The file file_input_raw was rewound 189 times rgb_lagged_sum| 16| 1000000| 200|0.02760994| PASSED # The file file_input_raw was rewound 202 times rgb_lagged_sum| 17| 1000000| 200|0.14079614| PASSED # The file file_input_raw was rewound 217 times rgb_lagged_sum| 18| 1000000| 200|0.23268911| PASSED # The file file_input_raw was rewound 231 times rgb_lagged_sum| 19| 1000000| 200|0.50758360| PASSED # The file file_input_raw was rewound 247 times rgb_lagged_sum| 20| 1000000| 200|0.66798605| PASSED # The file file_input_raw was rewound 263 times rgb_lagged_sum| 21| 1000000| 200|0.13844382| PASSED # The file file_input_raw was rewound 281 times rgb_lagged_sum| 22| 1000000| 200|0.44084188| PASSED # The file file_input_raw was rewound 299 times rgb_lagged_sum| 23| 1000000| 200|0.90104158| PASSED # The file file_input_raw was rewound 317 times rgb_lagged_sum| 24| 1000000| 200|0.80347842| PASSED # The file file_input_raw was rewound 337 times rgb_lagged_sum| 25| 1000000| 200|0.07703809| PASSED # The file file_input_raw was rewound 357 times rgb_lagged_sum| 26| 1000000| 200|0.69854447| PASSED # The file file_input_raw was rewound 377 times rgb_lagged_sum| 27| 1000000| 200|0.26315861| PASSED # The file file_input_raw was rewound 399 times rgb_lagged_sum| 28| 1000000| 200|0.58852190| PASSED # The file file_input_raw was rewound 421 times rgb_lagged_sum| 29| 1000000| 200|0.42224280| PASSED # The file file_input_raw was rewound 445 times rgb_lagged_sum| 30| 1000000| 200|0.18274721| PASSED # The file file_input_raw was rewound 468 times rgb_lagged_sum| 31| 1000000| 200|0.18647377| PASSED # The file file_input_raw was rewound 493 times rgb_lagged_sum| 32| 1000000| 200|0.83130153| PASSED # The file file_input_raw was rewound 493 times rgb_kstest_test| 0| 10000| 2000|0.05507241| PASSED # The file file_input_raw was rewound 494 times dab_bytedistrib| 0| 51200000| 2|0.22222222| PASSED # The file file_input_raw was rewound 494 times dab_dct| 256| 50000| 2|0.98359769| PASSED Preparing to run test 207. ntuple = 0 # The file file_input_raw was rewound 495 times dab_filltree| 32| 15000000| 2|0.75609132| PASSED dab_filltree| 32| 15000000| 2|1.00000000| FAILED Preparing to run test 208. ntuple = 0 # The file file_input_raw was rewound 495 times dab_filltree2| 0| 5000000| 2|1.00000000| FAILED dab_filltree2| 1| 5000000| 2|1.00000000| FAILED Preparing to run test 209. ntuple = 0 # The file file_input_raw was rewound 496 times dab_monobit2| 12| 65000000| 2|1.00000000| FAILED ``` ## Remove ```bash sudo rmmod pico_rng ``` # About the random number generator Raspberry Pi Pico has a Ring Oscillator (ROSC) that produces a random bit, despite that it is not intended for secure applications. In general, ROSC produce weak/correlated random bits. Thus, we increase the randomness by adding more entropy sources: - A random word produced by ROSC. - A current timestamp. - A read from an open GPIO pin. The produced random word is then passed through a whitener. In particular, the Fowler–Noll–Vo hash function is used as a whitener. Finally, the word is packed into a byte array and returned to the host. The performance of this RNG can be analyzed with [pico_rng_analyze.py](firmware/pico_rng_analyze.py) script and typical tools, such as `ent`, `rngtest` or `dierharder`. See results above. ### License This project is licensed under the BSD 3-Clause "New" or "Revised" License - see the [LICENSE.md](LICENSE.md) file for details ### References * https://github.com/raspberrypi/pico-examples/tree/master/usb/device/dev_lowlevel