├── .gitignore ├── Makefile ├── README.md ├── generate_constant_stream.c ├── tester.c ├── true_random.c └── true_random.h /.gitignore: -------------------------------------------------------------------------------- 1 | tester 2 | generate_constant_stream 3 | true_random.o 4 | 5 | ####################################################### 6 | 7 | # Created by https://www.gitignore.io/api/linux,c 8 | 9 | ### Linux ### 10 | *~ 11 | 12 | # temporary files which can be created if a process still has a handle open of a deleted file 13 | .fuse_hidden* 14 | 15 | # KDE directory preferences 16 | .directory 17 | 18 | # Linux trash folder which might appear on any partition or disk 19 | .Trash-* 20 | 21 | 22 | ### C ### 23 | # Object files 24 | *.o 25 | *.ko 26 | *.obj 27 | *.elf 28 | 29 | # Precompiled Headers 30 | *.gch 31 | *.pch 32 | 33 | # Libraries 34 | *.lib 35 | *.a 36 | *.la 37 | *.lo 38 | 39 | # Shared objects (inc. Windows DLLs) 40 | *.dll 41 | *.so 42 | *.so.* 43 | *.dylib 44 | 45 | # Executables 46 | *.exe 47 | *.out 48 | *.app 49 | *.i*86 50 | *.x86_64 51 | *.hex 52 | 53 | # Debug files 54 | *.dSYM/ 55 | *.su 56 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: true_random.o tester generate_constant_stream 2 | 3 | tester: tester.c true_random.o 4 | 5 | generate_constant_stream: generate_constant_stream.c true_random.o 6 | 7 | true_random.o: true_random.c true_random.h 8 | 9 | clean: 10 | rm -f tester generate_constant_stream true_random.o 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | True Random 2 | =========== 3 | 4 | > Use coin flipping to make an arguably true random number generator 5 | 6 | Most random number generation libraries are actually PRNGs (Pseudo Random Number Generators), since computers are basically deterministic. 7 | The library presented in this repository, however, uses the age-old concept of coin flipping along with naturally occuring delays, that occur on a multi-process system, to generate "truly" random numbers. 8 | 9 | NOTE(!) 10 | ------- 11 | 12 | I have learnt (through multiple helpful comments on [Reddit](https://www.reddit.com/r/programming/comments/4ipsg8/generate_truly_random_numbers/) and [HN](https://news.ycombinator.com/item?id=11667934)) that the method proposed in this is not really random (in the sense of the word: if everything else is same, then will the value be same). Hence, I would like to suggest this technique as only a way to generate some amount of entropy, and to combine it with other sources, and not use this for anything other than recreational purposes. In other words, this is a "clever trick" but is unusable for any kind of serious work. 13 | 14 | Method 15 | ------ 16 | 17 | ### `get_bit()` 18 | 19 | A tight loop which constantly flips a single bit (serving as a coin) is run for a millisecond. 20 | Since the number of times it would be able to flip the bit changes due to random fluctuations in time due to context switching of processes, this generates an arguably truly random bit (I would love to see a PoC that shows otherwise, however). 21 | 22 | While it can be agreed that context switching is a deterministic process, we know that practically, the different clocks that exist due to the numerous hardware devices attached to a system, each run at a separate speed. This leads to differing boot times each time, even if hardware remains unchanged. Due to similar reasons, what would be deterministic (the context switching), now has an extra spanner thrown in its works: the clock skew. This means it would basically be impossible (again, I'd love for someone to prove me wrong) to recreate the exact conditions that led to the production of the said bit. This means that the bit is no longer deterministic. Hence, the bit is "truly" random. 23 | 24 | ### `get_fair_bit()` 25 | 26 | Since the above generated bit might not actually have 50-50 probability of 0 and 1, we use the following table to normalize the probabilities. We repeatedly sample `get_bit()` twice, and decide based upon the results. 27 | 28 | | First Bit | Second Bit | Result | 29 | |-----------|------------|--------------| 30 | | 0 | 0 | Sample again | 31 | | 0 | 1 | Return 0 | 32 | | 1 | 0 | Return 1 | 33 | | 1 | 1 | Sample again | 34 | 35 | Assuming that each bit from `get_bit()` is independent of the previous bit, `get_fair_bit()` will return a bit with 50-50 probability of 0 or 1 36 | 37 | ### `true_random()` 38 | 39 | By concatenating 31 values from `get_fair_bit()`, we get the binary representation of a number betweeen 0 and `TRUE_RAND_MAX` (2147483647 = 2^32 - 1). This value is returned as a `long int`, to get a signature similar to `random()` from `stdlib.h`. 40 | 41 | Usage 42 | ----- 43 | 44 | Compile the code by running `make`. This ends up creating a bunch of files: 45 | 46 | + true_random.o 47 | + tester 48 | + generate_constant_stream 49 | 50 | Test the random number generator using `./tester`. Generate an unending stream of data using `./generate_constant_stream`. 51 | 52 | If you wish to use the generator in your own program, simply copy `true_random.o` and `true_random.h` into your project, and use the `true_random()` function after a `#include "true_random.h"`. 53 | 54 | License 55 | ------- 56 | 57 | This software is licensed under the [MIT License](http://jay.mit-license.org/2016) 58 | 59 | TODO 60 | ---- 61 | 62 | Run through standard RNG tests. -------------------------------------------------------------------------------- /generate_constant_stream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "true_random.h" 3 | 4 | int main() { 5 | while(1) { 6 | unsigned char n = 0; 7 | int i; 8 | for ( i = 0 ; i < 8; i++ ) { 9 | n *= 2; n += get_fair_bit(); 10 | } 11 | write(STDOUT_FILENO, &n, 1); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tester.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "true_random.h" 3 | 4 | int main() { 5 | int i; 6 | for ( i = 0 ; i < 10; i++ ) 7 | printf("%ld\n", true_random()); 8 | } 9 | -------------------------------------------------------------------------------- /true_random.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "true_random.h" 3 | 4 | const long MILLISEC_TO_NANOSEC = 1000000; 5 | const long MAX_NANOSEC = 1000000000; 6 | const long TRUE_RAND_MAX = 2147483647; 7 | 8 | static int get_bit() { 9 | struct timespec tp1, tp2; 10 | clock_gettime(CLOCK_MONOTONIC, &tp1); 11 | clock_gettime(CLOCK_MONOTONIC, &tp2); 12 | int bit = 0; 13 | long nanosec_cross_value = (tp1.tv_nsec + MILLISEC_TO_NANOSEC) % MAX_NANOSEC; 14 | while ( tp2.tv_nsec < nanosec_cross_value ) { 15 | bit = !bit; 16 | clock_gettime(CLOCK_MONOTONIC, &tp2); 17 | } 18 | return bit; 19 | } 20 | 21 | int get_fair_bit() { 22 | while (1) { 23 | int bit = get_bit(); 24 | if ( bit != get_bit() ) { 25 | return bit; 26 | } 27 | } 28 | } 29 | 30 | long true_random() { 31 | int i; 32 | long n = 0; 33 | for ( i = 0 ; i < 31 ; i++ ) { 34 | n *= 2; n += get_fair_bit(); 35 | } 36 | return n; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /true_random.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern const long TRUE_RAND_MAX; 4 | int get_fair_bit(); 5 | long true_random(); 6 | 7 | --------------------------------------------------------------------------------