SCPRNG - Simple cryptographic pseudo-random number generator library based on ChaCha20 stream cipher
- The generator should be deterministic - given the same inputs, always produces the same outputs, even on different platforms with different operating systems and architectures.
- The only initial setting - a 64-byte key. Thus, two different machines will produce the same numbers only if they both know the initial key.
- The generator should pass most of the known statistical tests and should have very high entropy.
- The generator should be small and fast, without external entropy sources that can slow down (or even temporarily block) number generation.
CPU: 12th Gen Intel(R) Core(TM) i9-12900H, 2.5 GHz
Performance: 248 MB/s
Command to check: Use below command with "-t" flag to generate 1 GB of random data using one CPU core and "scprng_gen" will print the performance.
$ taskset --cpu-list 0 ./scprng_gen -c 268435456 -o test.file -t
The operation took 4.1209 seconds.
The generation number speed: 260561420.1634 bytes per seconds.
Input: 64-byte secret key
Initial seed calculation: SHA3_384(key) = 48-byte seed
ChaCha20 parameters: encryption_key = seed[31:0], nonce = seed[43:32], counter = seed[47:44]
- Generate 64 bytes using ChaCha20 where input data is the initial secret key and the parameters described above. The input data of the next ChaCha20 operation will be an output of the previous one. Increment the counter after each ChaCha20 operation.
- Reseed after 512 generated bytes (8 cycles of step 1) - SHA3_384(last_chacha20_output). Update ChaCha20 parameters based on the new seed. Continue with the step 1.
The interface is pretty simple - only one function where you need to specify output array, how many numbers should be generated and the initial secret key. The output array should be allocated.
int scprng_rand_numbers(uint32_t *numbers,
uint32_t numbers_count,
uint8_t key[64]);
The generator doesn't comply with any national and international standards for cryptographic random number generators (CRNG) including "NIST Special Publication 800-90A Revision 1". Thus, if your application requires to be in line with some of these standards, the author of this repository doesn't garantee that this library or algorithm itself can meet this requirement. Use this library at your own risk (see "License" section).
MIT License, see the "LICENSE" file.
- make
- cmake
- mbedtls project include directory and libmbedcrypto compiled library for your target OS and architecture. libmbedcrypto is used for ChaCha20 encryption and SHA3 hashsum.
$ mkdir build && cd build
$ cmake -DCMAKE_BUILD_TYPE=Release -DMBEDCRYPTO_INCLUDE_DIR=./mbedtls/include -DMBEDCRYPTO_LIBRARY=./mbedtls/build/library/libmbedcrypto.a ..
$ make
Output in build directory:
- libscprng_${version}.a - static library
- libscprng_${version}.so - dynamic library, an extension is ".dll" for Windows
- scprng_gen - executable, generator utility, an extension is ".exe" for Windows
No automated tests were implemented. However, "test/test.sh" script generates big file using "scprng_gen" and run it through "ent" and "dieharder" utilities.
Pre-requirements:
- Install "unzip " and "dieharder" on the system, e.g. on Ubuntu:
$ sudo apt-get install unzip dieharder
- "ent" utility will be downloaded by the test script
Steps:
$ cd test
$ ./test.sh
ENT BITS MODE (Test file without specifying key)
Entropy = 1.000000 bits per bit.
Optimum compression would reduce the size
of this 300000000 bit file by 0 percent.
Chi square distribution for 300000000 samples is 2.13, and randomly
would exceed this value 14.48 percent of the times.
Arithmetic mean value of data bits is 0.5000 (0.5 = random).
Monte Carlo value for Pi is 3.142165760 (error 0.02 percent).
Serial correlation coefficient is 0.000015 (totally uncorrelated = 0.0).
ENT NORMAL MODE (Test file without specifying key)
Entropy = 7.999995 bits per byte.
Optimum compression would reduce the size
of this 37500000 byte file by 0 percent.
Chi square distribution for 37500000 samples is 256.50, and randomly
would exceed this value 46.19 percent of the times.
Arithmetic mean value of data bytes is 127.4844 (127.5 = random).
Monte Carlo value for Pi is 3.142165760 (error 0.02 percent).
Serial correlation coefficient is -0.000113 (totally uncorrelated = 0.0).
DIEHARDER (Test file without specifying key)
#=============================================================================#
# dieharder version 3.31.1 Copyright 2003 Robert G. Brown #
#=============================================================================#
rng_name | filename |rands/second|
mt19937| test.file| 2.19e+08 |
#=============================================================================#
test_name |ntup| tsamples |psamples| p-value |Assessment
#=============================================================================#
diehard_birthdays| 0| 100| 100|0.49054607| PASSED
diehard_operm5| 0| 1000000| 100|0.86964727| PASSED
diehard_rank_32x32| 0| 40000| 100|0.49259317| PASSED
diehard_rank_6x8| 0| 100000| 100|0.62077524| PASSED
diehard_bitstream| 0| 2097152| 100|0.09566472| PASSED
diehard_opso| 0| 2097152| 100|0.65288328| PASSED
diehard_oqso| 0| 2097152| 100|0.35362104| PASSED
diehard_dna| 0| 2097152| 100|0.57261112| PASSED
diehard_count_1s_str| 0| 256000| 100|0.30364371| PASSED
diehard_count_1s_byt| 0| 256000| 100|0.96815182| PASSED
diehard_parking_lot| 0| 12000| 100|0.21218600| PASSED
diehard_2dsphere| 2| 8000| 100|0.22589368| PASSED
diehard_3dsphere| 3| 4000| 100|0.40550381| PASSED
diehard_squeeze| 0| 100000| 100|0.85962163| PASSED
diehard_sums| 0| 100| 100|0.01124735| PASSED
diehard_runs| 0| 100000| 100|0.13114964| PASSED
diehard_runs| 0| 100000| 100|0.77103888| PASSED
diehard_craps| 0| 200000| 100|0.99456300| PASSED
diehard_craps| 0| 200000| 100|0.30184782| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.46745487| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.34962529| PASSED
sts_monobit| 1| 100000| 100|0.00058781| WEAK
sts_runs| 2| 100000| 100|0.82676559| PASSED
sts_serial| 1| 100000| 100|0.47969681| PASSED
sts_serial| 2| 100000| 100|0.97298578| PASSED
sts_serial| 3| 100000| 100|0.71449066| PASSED
sts_serial| 3| 100000| 100|0.21593869| PASSED
sts_serial| 4| 100000| 100|0.50874698| PASSED
sts_serial| 4| 100000| 100|0.51427995| PASSED
sts_serial| 5| 100000| 100|0.12560062| PASSED
sts_serial| 5| 100000| 100|0.01826805| PASSED
sts_serial| 6| 100000| 100|0.83774050| PASSED
sts_serial| 6| 100000| 100|0.09711059| PASSED
sts_serial| 7| 100000| 100|0.53851169| PASSED
sts_serial| 7| 100000| 100|0.58696592| PASSED
sts_serial| 8| 100000| 100|0.19883728| PASSED
sts_serial| 8| 100000| 100|0.62786782| PASSED
sts_serial| 9| 100000| 100|0.58029191| PASSED
sts_serial| 9| 100000| 100|0.50633799| PASSED
sts_serial| 10| 100000| 100|0.29434859| PASSED
sts_serial| 10| 100000| 100|0.63036372| PASSED
sts_serial| 11| 100000| 100|0.98372474| PASSED
sts_serial| 11| 100000| 100|0.69662053| PASSED
sts_serial| 12| 100000| 100|0.91452477| PASSED
sts_serial| 12| 100000| 100|0.73823573| PASSED
sts_serial| 13| 100000| 100|0.66135501| PASSED
sts_serial| 13| 100000| 100|0.52772917| PASSED
sts_serial| 14| 100000| 100|0.94538747| PASSED
sts_serial| 14| 100000| 100|0.99309028| PASSED
sts_serial| 15| 100000| 100|0.91289273| PASSED
sts_serial| 15| 100000| 100|0.90006300| PASSED
sts_serial| 16| 100000| 100|0.50636437| PASSED
sts_serial| 16| 100000| 100|0.59220008| PASSED
rgb_bitdist| 1| 100000| 100|0.75615018| PASSED
rgb_bitdist| 2| 100000| 100|0.77263636| PASSED
rgb_bitdist| 3| 100000| 100|0.86340094| PASSED
rgb_bitdist| 4| 100000| 100|0.89209395| PASSED
rgb_bitdist| 5| 100000| 100|0.85269983| PASSED
rgb_bitdist| 6| 100000| 100|0.65608923| PASSED
rgb_bitdist| 7| 100000| 100|0.57148795| PASSED
rgb_bitdist| 8| 100000| 100|0.47122576| PASSED
rgb_bitdist| 9| 100000| 100|0.99367397| PASSED
rgb_bitdist| 10| 100000| 100|0.95909439| PASSED
rgb_bitdist| 11| 100000| 100|0.57631857| PASSED
rgb_bitdist| 12| 100000| 100|0.78891168| PASSED
rgb_minimum_distance| 2| 10000| 1000|0.48320889| PASSED
rgb_minimum_distance| 3| 10000| 1000|0.79844305| PASSED
rgb_minimum_distance| 4| 10000| 1000|0.67147875| PASSED
rgb_minimum_distance| 5| 10000| 1000|0.17320502| PASSED
rgb_permutations| 2| 100000| 100|0.77504541| PASSED
rgb_permutations| 3| 100000| 100|0.80508847| PASSED
rgb_permutations| 4| 100000| 100|0.38243065| PASSED
rgb_permutations| 5| 100000| 100|0.01964174| PASSED
rgb_lagged_sum| 0| 1000000| 100|0.81917849| PASSED
rgb_lagged_sum| 1| 1000000| 100|0.77077762| PASSED
rgb_lagged_sum| 2| 1000000| 100|0.02833445| PASSED
rgb_lagged_sum| 3| 1000000| 100|0.38235768| PASSED
rgb_lagged_sum| 4| 1000000| 100|0.94455560| PASSED
rgb_lagged_sum| 5| 1000000| 100|0.75661038| PASSED
rgb_lagged_sum| 6| 1000000| 100|0.61843019| PASSED
rgb_lagged_sum| 7| 1000000| 100|0.50874762| PASSED
rgb_lagged_sum| 8| 1000000| 100|0.92002793| PASSED
rgb_lagged_sum| 9| 1000000| 100|0.99917644| WEAK
rgb_lagged_sum| 10| 1000000| 100|0.84064539| PASSED
rgb_lagged_sum| 11| 1000000| 100|0.43345012| PASSED
rgb_lagged_sum| 12| 1000000| 100|0.91344295| PASSED
rgb_lagged_sum| 13| 1000000| 100|0.68299530| PASSED
rgb_lagged_sum| 14| 1000000| 100|0.51876229| PASSED
rgb_lagged_sum| 15| 1000000| 100|0.52705733| PASSED
rgb_lagged_sum| 16| 1000000| 100|0.36116940| PASSED
rgb_lagged_sum| 17| 1000000| 100|0.29643828| PASSED
rgb_lagged_sum| 18| 1000000| 100|0.82172317| PASSED
rgb_lagged_sum| 19| 1000000| 100|0.05761174| PASSED
rgb_lagged_sum| 20| 1000000| 100|0.95964970| PASSED
rgb_lagged_sum| 21| 1000000| 100|0.03259833| PASSED
rgb_lagged_sum| 22| 1000000| 100|0.99385060| PASSED
rgb_lagged_sum| 23| 1000000| 100|0.78192220| PASSED
rgb_lagged_sum| 24| 1000000| 100|0.83909070| PASSED
rgb_lagged_sum| 25| 1000000| 100|0.63780446| PASSED
rgb_lagged_sum| 26| 1000000| 100|0.93982979| PASSED
rgb_lagged_sum| 27| 1000000| 100|0.08598246| PASSED
rgb_lagged_sum| 28| 1000000| 100|0.41984271| PASSED
rgb_lagged_sum| 29| 1000000| 100|0.97466275| PASSED
rgb_lagged_sum| 30| 1000000| 100|0.09005194| PASSED
rgb_lagged_sum| 31| 1000000| 100|0.79524873| PASSED
rgb_lagged_sum| 32| 1000000| 100|0.11240447| PASSED
rgb_kstest_test| 0| 10000| 1000|0.71293211| PASSED
dab_bytedistrib| 0| 51200000| 1|0.37383687| PASSED
dab_dct| 256| 50000| 1|0.54476212| PASSED
Preparing to run test 207. ntuple = 0
dab_filltree| 32| 15000000| 1|0.59348573| PASSED
dab_filltree| 32| 15000000| 1|0.34535965| PASSED
Preparing to run test 208. ntuple = 0
dab_filltree2| 0| 5000000| 1|0.38865552| PASSED
dab_filltree2| 1| 5000000| 1|0.81467901| PASSED
Preparing to run test 209. ntuple = 0
dab_monobit2| 12| 65000000| 1|0.83111544| PASSED
./test.sh: line 114: p: command not found
ENT BITS MODE (Test file with specifying key)
Entropy = 1.000000 bits per bit.
Optimum compression would reduce the size
of this 300000000 bit file by 0 percent.
Chi square distribution for 300000000 samples is 0.01, and randomly
would exceed this value 90.94 percent of the times.
Arithmetic mean value of data bits is 0.5000 (0.5 = random).
Monte Carlo value for Pi is 3.141645440 (error 0.00 percent).
Serial correlation coefficient is 0.000103 (totally uncorrelated = 0.0).
ENT NORMAL MODE (Test file with specifying key)
Entropy = 7.999995 bits per byte.
Optimum compression would reduce the size
of this 37500000 byte file by 0 percent.
Chi square distribution for 37500000 samples is 285.67, and randomly
would exceed this value 9.07 percent of the times.
Arithmetic mean value of data bytes is 127.5000 (127.5 = random).
Monte Carlo value for Pi is 3.141645440 (error 0.00 percent).
Serial correlation coefficient is 0.000127 (totally uncorrelated = 0.0).
DIEHARDER (Test file with specifying key)
#=============================================================================#
# dieharder version 3.31.1 Copyright 2003 Robert G. Brown #
#=============================================================================#
rng_name | filename |rands/second|
mt19937| test.file| 2.16e+08 |
#=============================================================================#
test_name |ntup| tsamples |psamples| p-value |Assessment
#=============================================================================#
diehard_birthdays| 0| 100| 100|0.92902982| PASSED
diehard_operm5| 0| 1000000| 100|0.35551709| PASSED
diehard_rank_32x32| 0| 40000| 100|0.07147839| PASSED
diehard_rank_6x8| 0| 100000| 100|0.74736383| PASSED
diehard_bitstream| 0| 2097152| 100|0.88607040| PASSED
diehard_opso| 0| 2097152| 100|0.30616074| PASSED
diehard_oqso| 0| 2097152| 100|0.13032179| PASSED
diehard_dna| 0| 2097152| 100|0.30983670| PASSED
diehard_count_1s_str| 0| 256000| 100|0.28267972| PASSED
diehard_count_1s_byt| 0| 256000| 100|0.93797039| PASSED
diehard_parking_lot| 0| 12000| 100|0.90061093| PASSED
diehard_2dsphere| 2| 8000| 100|0.97477750| PASSED
diehard_3dsphere| 3| 4000| 100|0.43589018| PASSED
diehard_squeeze| 0| 100000| 100|0.44668023| PASSED
diehard_sums| 0| 100| 100|0.25830125| PASSED
diehard_runs| 0| 100000| 100|0.89181475| PASSED
diehard_runs| 0| 100000| 100|0.03092652| PASSED
diehard_craps| 0| 200000| 100|0.80128560| PASSED
diehard_craps| 0| 200000| 100|0.53608891| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.76718939| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.34727558| PASSED
sts_monobit| 1| 100000| 100|0.40163778| PASSED
sts_runs| 2| 100000| 100|0.63666301| PASSED
sts_serial| 1| 100000| 100|0.86036694| PASSED
sts_serial| 2| 100000| 100|0.63584371| PASSED
sts_serial| 3| 100000| 100|0.25136950| PASSED
sts_serial| 3| 100000| 100|0.41356438| PASSED
sts_serial| 4| 100000| 100|0.48574935| PASSED
sts_serial| 4| 100000| 100|0.77018978| PASSED
sts_serial| 5| 100000| 100|0.23880794| PASSED
sts_serial| 5| 100000| 100|0.87257305| PASSED
sts_serial| 6| 100000| 100|0.99952090| WEAK
sts_serial| 6| 100000| 100|0.64588181| PASSED
sts_serial| 7| 100000| 100|0.81384648| PASSED
sts_serial| 7| 100000| 100|0.85379243| PASSED
sts_serial| 8| 100000| 100|0.91165600| PASSED
sts_serial| 8| 100000| 100|0.96490130| PASSED
sts_serial| 9| 100000| 100|0.44364812| PASSED
sts_serial| 9| 100000| 100|0.77084830| PASSED
sts_serial| 10| 100000| 100|0.15228597| PASSED
sts_serial| 10| 100000| 100|0.46420135| PASSED
sts_serial| 11| 100000| 100|0.61667368| PASSED
sts_serial| 11| 100000| 100|0.18728213| PASSED
sts_serial| 12| 100000| 100|0.55056134| PASSED
sts_serial| 12| 100000| 100|0.27426868| PASSED
sts_serial| 13| 100000| 100|0.66918798| PASSED
sts_serial| 13| 100000| 100|0.19598369| PASSED
sts_serial| 14| 100000| 100|0.81321045| PASSED
sts_serial| 14| 100000| 100|0.99888378| WEAK
sts_serial| 15| 100000| 100|0.25783381| PASSED
sts_serial| 15| 100000| 100|0.78606471| PASSED
sts_serial| 16| 100000| 100|0.04641213| PASSED
sts_serial| 16| 100000| 100|0.11525973| PASSED
rgb_bitdist| 1| 100000| 100|0.30376668| PASSED
rgb_bitdist| 2| 100000| 100|0.74214828| PASSED
rgb_bitdist| 3| 100000| 100|0.09678289| PASSED
rgb_bitdist| 4| 100000| 100|0.27346980| PASSED
rgb_bitdist| 5| 100000| 100|0.99935862| WEAK
rgb_bitdist| 6| 100000| 100|0.95750437| PASSED
rgb_bitdist| 7| 100000| 100|0.93938087| PASSED
rgb_bitdist| 8| 100000| 100|0.80791607| PASSED
rgb_bitdist| 9| 100000| 100|0.33546085| PASSED
rgb_bitdist| 10| 100000| 100|0.72044634| PASSED
rgb_bitdist| 11| 100000| 100|0.71504537| PASSED
rgb_bitdist| 12| 100000| 100|0.84814894| PASSED
rgb_minimum_distance| 2| 10000| 1000|0.36919312| PASSED
rgb_minimum_distance| 3| 10000| 1000|0.18548548| PASSED
rgb_minimum_distance| 4| 10000| 1000|0.74932853| PASSED
rgb_minimum_distance| 5| 10000| 1000|0.16927895| PASSED
rgb_permutations| 2| 100000| 100|0.78515220| PASSED
rgb_permutations| 3| 100000| 100|0.87635474| PASSED
rgb_permutations| 4| 100000| 100|0.11998414| PASSED
rgb_permutations| 5| 100000| 100|0.60387484| PASSED
rgb_lagged_sum| 0| 1000000| 100|0.77163114| PASSED
rgb_lagged_sum| 1| 1000000| 100|0.92739881| PASSED
rgb_lagged_sum| 2| 1000000| 100|0.10309911| PASSED
rgb_lagged_sum| 3| 1000000| 100|0.38977884| PASSED
rgb_lagged_sum| 4| 1000000| 100|0.83655643| PASSED
rgb_lagged_sum| 5| 1000000| 100|0.08303353| PASSED
rgb_lagged_sum| 6| 1000000| 100|0.26080557| PASSED
rgb_lagged_sum| 7| 1000000| 100|0.22200865| PASSED
rgb_lagged_sum| 8| 1000000| 100|0.26766012| PASSED
rgb_lagged_sum| 9| 1000000| 100|0.93614691| PASSED
rgb_lagged_sum| 10| 1000000| 100|0.89246797| PASSED
rgb_lagged_sum| 11| 1000000| 100|0.29055941| PASSED
rgb_lagged_sum| 12| 1000000| 100|0.01947262| PASSED
rgb_lagged_sum| 13| 1000000| 100|0.66688291| PASSED
rgb_lagged_sum| 14| 1000000| 100|0.82000209| PASSED
rgb_lagged_sum| 15| 1000000| 100|0.57499351| PASSED
rgb_lagged_sum| 16| 1000000| 100|0.56885503| PASSED
rgb_lagged_sum| 17| 1000000| 100|0.83431700| PASSED
rgb_lagged_sum| 18| 1000000| 100|0.84265293| PASSED
rgb_lagged_sum| 19| 1000000| 100|0.61711813| PASSED
rgb_lagged_sum| 20| 1000000| 100|0.87683450| PASSED
rgb_lagged_sum| 21| 1000000| 100|0.23213722| PASSED
rgb_lagged_sum| 22| 1000000| 100|0.59203379| PASSED
rgb_lagged_sum| 23| 1000000| 100|0.57359631| PASSED
rgb_lagged_sum| 24| 1000000| 100|0.50393763| PASSED
rgb_lagged_sum| 25| 1000000| 100|0.62176339| PASSED
rgb_lagged_sum| 26| 1000000| 100|0.71523024| PASSED
rgb_lagged_sum| 27| 1000000| 100|0.66333255| PASSED
rgb_lagged_sum| 28| 1000000| 100|0.97354819| PASSED
rgb_lagged_sum| 29| 1000000| 100|0.91857114| PASSED
rgb_lagged_sum| 30| 1000000| 100|0.41347628| PASSED
rgb_lagged_sum| 31| 1000000| 100|0.59485630| PASSED
rgb_lagged_sum| 32| 1000000| 100|0.16672451| PASSED
rgb_kstest_test| 0| 10000| 1000|0.08469036| PASSED
dab_bytedistrib| 0| 51200000| 1|0.47410178| PASSED
dab_dct| 256| 50000| 1|0.85245522| PASSED
Preparing to run test 207. ntuple = 0
dab_filltree| 32| 15000000| 1|0.18141777| PASSED
dab_filltree| 32| 15000000| 1|0.86672825| PASSED
Preparing to run test 208. ntuple = 0
dab_filltree2| 0| 5000000| 1|0.91102894| PASSED
dab_filltree2| 1| 5000000| 1|0.82881132| PASSED
Preparing to run test 209. ntuple = 0
dab_monobit2| 12| 65000000| 1|0.53314002| PASSED
ALL TESTS ARE DONE. PLEASE CHECK OUTPUT.