Dev random: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Dev random
| Old | New | Line | |
|---|---|---|---|
| 1 | + | `/dev/random` and `/dev/urandom` are two pseudo character devices providing user-land access to random data generated by the system. Following the paradigm “everything is a file” they usually exist on Unix and *nix-based operating systems, like [[Linux]] or [[FreeBSD]]. | |
| 2 | + | ||
| 3 | + | The `u` in `urandom` stands for “unlimited”. Theoretically there is no limit on how much data one may obtain `/dev/urandom`. | |
| 4 | + | ||
| 5 | + | ## quality | |
| 6 | + | ||
| 7 | + | On Linux the kernel will occasonially gather “environmental noise” like memory, disc, network throughput, or dumps from the kernel, as well as cryptography hardware if present and enabled. | |
| 8 | + | ||
| 9 | + | The key difference between `/dev/random` versus `/dev/urandom` is whether a threshold of enough entropy has to be reached before random numbers are generated. | |
| 10 | + | ||
| 11 | + | - [Reading](https://www.freepascal.org/docs-html/rtl/baseunix/fpread.html) from `/dev/random` will be put on hold if the kernel has not gathered enough entropy to provide the requested amount of data. | |
| 12 | + | - `/dev/urandom` on the other hand will not block to wait for more entropy. It will fall back to a pseudo-random number generator (PRNG) if and as long as there is a too small entropy pool. | |
| 13 | + | ||
| 14 | + | ## application | |
| 15 | + | ||
| 16 | + | ### seed | |
| 17 | + | ||
| 18 | + | Prior any call of [`system.random`](https://www.freepascal.org/docs-html/rtl/system/random.html) the programmer has to invoke [`system.randomize`](https://www.freepascal.org/docs-html/rtl/system/randomize.html) once, so [`system.randSeed`](https://www.freepascal.org/docs-html/rtl/system/randseed.html) provides a “random” value for the PRNG. The [default implementation](https://gitlab.com/freepascal.org/fpc/source/-/tree/release_3_2_0/rtl/linux/system.pp#L231-234) uses the system clock for this, since this a value sort of available on all supported platforms. However, its predictability is kind of unpleasant, although the argument `system.randomize`’s behavior should not differ significantly regarding quality and speed among platforms is reasonable. If we want to, we can shadow `system.randomize` by our own “better” implementation, while still relying on the fast Mersenne-Twister PRNG the standard [[RTL|run-time library]] comes with. | |
| 19 | + | ||
| 20 | + | ``` pascal | |
| 21 | + | {$ifdef UNIX} | |
| 22 | + | (** | |
| 23 | + | initializes PRNG with data read from /dev/random | |
| 24 | + | ||
| 25 | + | Randomize initializes the pseudo-random number generator | |
| 26 | + | by storing a value read from /dev/random to system.randSeed. | |
| 27 | + | If reading fails, system.randomize will be used instead. | |
| 28 | + | *) | |
| 29 | + | procedure randomize; | |
| 30 | + | const | |
| 31 | + | /// file name for random(4) device | |
| 32 | + | randomDeviceName = '/dev/random'; | |
| 33 | + | var | |
| 34 | + | /// reading buffer | |
| 35 | + | // same type as system.randSeed | |
| 36 | + | randomNumber: cardinal; | |
| 37 | + | /// file handle | |
| 38 | + | randomReader: file of cardinal; | |
| 39 | + | begin | |
| 40 | + | assign(randomReader, randomDeviceName); | |
| 41 | + | {$push} | |
| 42 | + | // turn off run-time error generation | |
| 43 | + | {$IOChecks off} | |
| 44 | + | reset(randomReader); | |
| 45 | + | ||
| 46 | + | if IOResult() = 0 then | |
| 47 | + | begin | |
| 48 | + | // will possibly cause the error | |
| 49 | + | // EInOutError: Read past end of file | |
| 50 | + | // if /dev/random is depleted | |
| 51 | + | read(randomReader, randomNumber); | |
| 52 | + | ||
| 53 | + | if IOResult() = 0 then | |
| 54 | + | begin | |
| 55 | + | system.randSeed := randomNumber; | |
| 56 | + | end | |
| 57 | + | else | |
| 58 | + | begin | |
| 59 | + | // do not call oneself => fully qualified identifier | |
| 60 | + | system.randomize; | |
| 61 | + | end; | |
| 62 | + | ||
| 63 | + | close(randomReader); | |
| 64 | + | end | |
| 65 | + | {$pop} | |
| 66 | + | else | |
| 67 | + | begin | |
| 68 | + | // do not call oneself => fully qualified identifier | |
| 69 | + | system.randomize; | |
| 70 | + | end; | |
| 71 | + | end; | |
| 72 | + | {$else} | |
| 73 | + | {$hint program does not use randomize based on /dev/random} | |
| 74 | + | {$endif} | |
| 75 | + | ``` | |
| 76 | + | ||
| 77 | + | When using [[gmp]], the same applies. | |
| 78 | + | ||
| 79 | + | ```delphi | |
| 80 | + | program gmpRandomDemo(input, output, stderr); | |
| 81 | + | ||
| 82 | + | // objFPC mode for try..finally-construct | |
| 83 | + | {$mode objFPC} | |
| 84 | + | {$typedAddress on} | |
| 85 | + | ||
| 86 | + | uses | |
| 87 | + | // familiarize with C types | |
| 88 | + | cTypes, | |
| 89 | + | // familiarize with exception classes | |
| 90 | + | sysUtils, | |
| 91 | + | // use GNU multiple precision arithmetic library | |
| 92 | + | gmp; | |
| 93 | + | ||
| 94 | + | const | |
| 95 | + | /// file name for random(4) device | |
| 96 | + | randomFileName = '/dev/random'; | |
| 97 | + | ||
| 98 | + | var | |
| 99 | + | /// reading buffer | |
| 100 | + | randomNumber: CULong; | |
| 101 | + | /// file handle for random number source | |
| 102 | + | randomReader: file of CULong; | |
| 103 | + | /// keeps GMP random number generator state | |
| 104 | + | prngState: randState_T; | |
| 105 | + | /// some arbitrary number | |
| 106 | + | i: MPZ_T; | |
| 107 | + | begin | |
| 108 | + | MP_randInit_MT(prngState); | |
| 109 | + | assign(randomReader, randomFileName); | |
| 110 | + | try | |
| 111 | + | reset(randomReader); | |
| 112 | + | try | |
| 113 | + | try | |
| 114 | + | read(randomReader, randomNumber); | |
| 115 | + | except on eInOutError do | |
| 116 | + | begin | |
| 117 | + | randomize; | |
| 118 | + | randomNumber := random(high(int64)); | |
| 119 | + | end; | |
| 120 | + | end; | |
| 121 | + | finally | |
| 122 | + | close(randomReader); | |
| 123 | + | end; | |
| 124 | + | MP_randSeed_UI(prngState, randomNumber); | |
| 125 | + | ||
| 126 | + | MPZ_init(i); | |
| 127 | + | try | |
| 128 | + | MPZ_uRandomB(i, prngState, 256); | |
| 129 | + | MP_printF('%Zd' + lineEnding, @i); | |
| 130 | + | finally | |
| 131 | + | MPZ_clear(i); | |
| 132 | + | end; | |
| 133 | + | finally | |
| 134 | + | MP_randClear(prngState); | |
| 135 | + | end; | |
| 136 | + | end. | |
| 137 | + | ``` | |
| 138 | + | ||
| 139 | + | ### multiple values | |
| 140 | + | ||
| 141 | + | Multiple random values can be read by utilizing [`system.blockRead`](https://www.freepascal.org/docs-html/rtl/system/blockread.html): | |
| 142 | + | ||
| 143 | + | ```delphi | |
| 144 | + | program multipleRandomValuesDemo(input, output, stdErr); | |
| 145 | + | ||
| 146 | + | // include objpas unit | |
| 147 | + | {$mode objFPC} | |
| 148 | + | ||
| 149 | + | uses | |
| 150 | + | // familiarize with exception classes | |
| 151 | + | sysUtils; | |
| 152 | + | ||
| 153 | + | const | |
| 154 | + | /// file name for urandom(4) device | |
| 155 | + | randomDeviceName = '/dev/urandom'; | |
| 156 | + | ||
| 157 | + | type | |
| 158 | + | /// base type for numbers in this program | |
| 159 | + | number = dWord; | |
| 160 | + | ||
| 161 | + | var | |
| 162 | + | /// file handle for random number source | |
| 163 | + | randomFile: file of number; | |
| 164 | + | /// an array of numbers | |
| 165 | + | population: packed array[0..5] of number; | |
| 166 | + | /// temporary iterator variable | |
| 167 | + | i: number; | |
| 168 | + | ||
| 169 | + | begin | |
| 170 | + | assignFile(randomFile, randomDeviceName); | |
| 171 | + | try | |
| 172 | + | reset(randomFile); | |
| 173 | + | try | |
| 174 | + | blockRead(randomFile, population, length(population)); | |
| 175 | + | except on eInOutError do | |
| 176 | + | exitCode := 1; | |
| 177 | + | end; | |
| 178 | + | finally | |
| 179 | + | closeFile(randomFile); | |
| 180 | + | end; | |
| 181 | + | ||
| 182 | + | for i in population do | |
| 183 | + | begin | |
| 184 | + | writeLn(i:10); | |
| 185 | + | end; | |
| 186 | + | end. | |
| 187 | + | ``` | |
| 188 | + | ||
| 189 | + | ## see also | |
| 190 | + | ||
| 191 | + | - [`/dev/random` on Wikipedia, the free online encyclopedia](https://en.wikipedia.org/wiki//dev/random) | |
| 192 | + | - [[Generating Random Numbers|generating random numbers]], alternative algorithms | |
| 193 | + | - [“random state seeding” in GMP’s documentation](https://gmplib.org/manual/Random-State-Seeding.html#Random-State-Seeding) | |
| 194 | + | ||
| 195 | + | --- | |
| 196 | + | *From the Free Pascal wiki, page "Dev random", as it stood on 2022-09-28 after a change by Kai Burghardt, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* | |
| 197 | + | Category: Code |
