Difference between revisions of "Dev random"

From Lazarus wiki
Jump to navigationJump to search
(complete rewrite)
Line 1: Line 1:
 +
{{DISPLAYTITLE:/dev/random}}
 
{{Dev random}}
 
{{Dev random}}
 +
<code>/dev/random</code> and <code>/dev/urandom</code> 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]].
  
== Summary ==
+
The <code>u</code> in <code>urandom</code> stands for the Greek letter μ, meaning “micro”.
<code>/dev/random</code> and <code>/dev/urandom</code> are two Unix and *nix based devices that process memory, disc, network dumps from the kernel (as well as cryptography hardware, if present and enabled) to generate (pseudo)random numbers.
+
The randomness of <code>/dev/urandom</code> is reduced, in comparison to <code>/dev/random</code>.
  
The difference between /dev/random and /dev/urandom is whether the device is "blocking" or "non blocking":
+
== quality ==
 +
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.
  
* when /dev/random is giving random data, it will wait until it has something that is random enough to give us before returning the data.  
+
The key difference between <code>/dev/random</code> versus <code>/dev/urandom</code> is, whether a threshold of enough entropy has to be reached, before random numbers are generated.
* /dev/urandom does not stop when it runs out of random data, and will also return data that is less random. This way we will not need to wait until any type of activity will occur, but '''randomness is reduced'''.
+
* {{Doc|package=RTL|unit=baseunix|identifier=fpread|text=Reading}} from <code>/dev/random</code> will be put on hold, if the kernel has not gathered enough entropy to provide the requested amount of data.
 +
* <code>/dev/urandom</code> 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.
  
Using <code>/dev/random</code> or <code>/dev/urandom</code> allows us to use a "random" like seed ([[doc:rtl/system/random.html|randseed]]) for performing "random" tasks with the [[doc:rtl/system/random.html|random]] function.
+
== application ==
 
+
=== seed ===
== Examples ==
+
Prior any call of {{Doc|package=RTL|unit=system|identifier=random|text=<syntaxhighlight lang="pascal" enclose="none">system.random</syntaxhighlight>}} the programmer has to invoke {{Doc|package=RTL|unit=system|identifier=randomize|text=<syntaxhighlight lang="pascal" enclose="none">system.randomize</syntaxhighlight>}} once, so {{Doc|package=RTL|unit=system|identifier=randseed|text=<syntaxhighlight lang="pascal" enclose="none">system.randSeed</syntaxhighlight>}} provides a “random” value for the PRNG.
In order to make Free Pascal work with /dev/random and /dev/random we can write the following code:
+
The [https://svn.freepascal.org/cgi-bin/viewvc.cgi/tags/release_3_0_4/rtl/linux/system.pp?view=markup#l142 default implementation] uses the system clock for this, since this a value sort of available on all supported platforms.
<syntaxhighlight>
+
However, its predictability is kind of unpleasant, although the argument <syntaxhighlight lang="pascal" enclose="none">system.randomize</syntaxhighlight>'s behavior should not differ significantly regarding quality and speed among platforms is reasonable.
procedure RandomSeed;
+
If we want to, we can shadow <syntaxhighlight lang="pascal" enclose="none">system.randomize</syntaxhighlight> by our own “better” implementation, while still relying on the fast Mersenne-Twister PRNG the standard [[RTL|runtime library]] comes with.
 +
<syntaxhighlight lang="pascal" line start="3">
 +
{$ifdef UNIX}
 +
(**
 +
initializes PRNG with data read from /dev/random
 +
 +
Randomize initializes the pseudo-random number generator
 +
by storing a value read from /dev/random to system.randSeed.
 +
If reading fails, system.randomize will be used instead.
 +
*)
 +
procedure randomize;
 +
const
 +
/// file name for random(4) device
 +
randomDeviceName = '/dev/random';
 
var
 
var
  f : file of integer;
+
/// reading buffer
  i : integer;
+
// same type as system.randSeed
 
+
randomNumber: cardinal;
 +
/// file handle
 +
randomReader: file of cardinal;
 
begin
 
begin
  i        := 0;
+
assign(randomReader, randomDeviceName);
   filemode := 0;
+
{$push}
  AssignFile(f, '/dev/urandom');
+
// turn off run-time error generation
  reset (f,1);
+
{$IOChecks off}
  read (f,i);
+
reset(randomReader);
  CloseFile (f);
+
  RandSeed := i;
+
if IOResult() <> 0 then
 +
begin
 +
// will possibly cause the error
 +
//   EInOutError: Read past end of file
 +
// if /dev/random is depleted
 +
read(randomReader, randomNumber);
 +
 +
if IOResult() <> 0 then
 +
begin
 +
system.randSeed := randomNumber;
 +
end
 +
else
 +
begin
 +
// do not call one-self => fully qualified identfier
 +
system.randomize;
 +
end;
 +
 +
close(randomReader);
 +
end
 +
{$pop}
 +
else
 +
begin
 +
// do not call one-self => fully qualified identfier
 +
system.randomize;
 +
end;
 
end;
 
end;
 +
{$else}
 +
{$hint program does not use randomize based on /dev/random}
 +
{$endif}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== Explanation ==
+
When using [[gmp]], the same applies.
Like almost everything else in *nix like OSes, everything is a file! So, we access urandom as a file and we read only one integer at a time.
+
<syntaxhighlight lang="delphi" line highlight="15-17,19-25,29-45,54-56">
 +
program gmpRandomDemo(input, output, stderr);
 +
 
 +
// objFPC mode for try..finally-construct
 +
{$mode objFPC}
 +
{$typedAddress on}
 +
 
 +
uses
 +
// familiarze with C types
 +
cTypes,
 +
// familiarize with exception classes
 +
sysUtils,
 +
// use GNU multi-precision library
 +
gmp;
 +
 
 +
const
 +
/// file name for random(4) device
 +
randomFileName = '/dev/random';
  
Then, we place the data we made as RandSeed (the seed number that the function random uses). And that's it, we have a random seeder for "random" usage.
 
== note ==
 
Note that if you need multiple random values it is possible to read multiple random values at once by doing a blockread.<br>
 
Here's an example:<br>
 
<syntaxhighlight>
 
program randomblock;
 
{$mode objfpc}
 
 
var
 
var
  f : file of dword;
+
/// reading buffer
  r : packed array[0..5] of dword;
+
randomNumber: CULong;
  i : dword;
+
/// file handle for random number source
 +
randomReader: file of CULong;
 +
/// keeps GMP random number generator state
 +
prngState: randState_T;
 +
/// some arbitrary number
 +
i: MPZ_T;
 
begin
 
begin
  AssignFile(f, '/dev/urandom');  
+
MP_randInit_MT(prngState);
  reset (f);
+
assign(randomReader, randomFileName);
  blockread (f,r,6);
+
try
  CloseFile (f);
+
reset(randomReader);
  for i in r do
+
try
    writeln(i);
+
try
 +
read(randomReader, randomNumber);
 +
except on eInOutError do
 +
begin
 +
randomize;
 +
randomNumber := random(high(int64));
 +
end;
 +
end;
 +
finally
 +
close(randomReader);
 +
end;
 +
MP_randSeed_UI(prngState, randomNumber);
 +
 +
MPZ_init(i);
 +
try
 +
MPZ_uRandomB(i, prngState, 256);
 +
MP_printF('%Zd' + lineEnding, @i);
 +
finally
 +
MPZ_clear(i);
 +
end;
 +
finally
 +
MP_randClear(prngState);
 +
end;
 
end.
 
end.
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== See also ==
+
== see also ==
* [[doc:rtl/system/random.html|random]]
+
* [https://en.wikipedia.org/wiki//dev/random <code>/dev/random</code> on Wikipedia, the free online encyclopedia]
* [[doc:rtl/system/random.html|randseed]]
+
* [[Generating Random Numbers|generating random numbers]], alternative algorithms
* [[Generating Random Numbers]]
+
* [https://gmplib.org/manual/Random-State-Seeding.html#Random-State-Seeding “random state seeding” in GMP's documentation]
 +
 
 +
[[Category:Code]]

Revision as of 22:46, 21 November 2018

Deutsch (de) English (en) français (fr)
/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.

The u in urandom stands for the Greek letter μ, meaning “micro”. The randomness of /dev/urandom is reduced, in comparison to /dev/random.

quality

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.

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.

  • Reading from /dev/random will be put on hold, if the kernel has not gathered enough entropy to provide the requested amount of data.
  • /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.

application

seed

Prior any call of system.random the programmer has to invoke system.randomize once, so system.randSeed provides a “random” value for the PRNG. The default implementation 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 runtime library comes with.

 3{$ifdef UNIX}
 4(**
 5	initializes PRNG with data read from /dev/random
 6	
 7	Randomize initializes the pseudo-random number generator
 8	by storing a value read from /dev/random to system.randSeed.
 9	If reading fails, system.randomize will be used instead.
10*)
11procedure randomize;
12const
13	/// file name for random(4) device
14	randomDeviceName = '/dev/random';
15var
16	/// reading buffer
17	// same type as system.randSeed
18	randomNumber: cardinal;
19	/// file handle
20	randomReader: file of cardinal;
21begin
22	assign(randomReader, randomDeviceName);
23	{$push}
24	// turn off run-time error generation
25	{$IOChecks off}
26	reset(randomReader);
27	
28	if IOResult() <> 0 then
29	begin
30		// will possibly cause the error
31		//   EInOutError: Read past end of file
32		// if /dev/random is depleted
33		read(randomReader, randomNumber);
34		
35		if IOResult() <> 0 then
36		begin
37			system.randSeed := randomNumber;
38		end
39		else
40		begin
41			// do not call one-self => fully qualified identfier
42			system.randomize;
43		end;
44		
45		close(randomReader);
46	end
47	{$pop}
48	else
49	begin
50		// do not call one-self => fully qualified identfier
51		system.randomize;
52	end;
53end;
54{$else}
55{$hint program does not use randomize based on /dev/random}
56{$endif}

When using gmp, the same applies.

 1program gmpRandomDemo(input, output, stderr);
 2
 3// objFPC mode for try..finally-construct
 4{$mode objFPC}
 5{$typedAddress on}
 6
 7uses
 8	// familiarze with C types
 9	cTypes,
10	// familiarize with exception classes
11	sysUtils,
12	// use GNU multi-precision library
13	gmp;
14
15const
16	/// file name for random(4) device
17	randomFileName = '/dev/random';
18
19var
20	/// reading buffer
21	randomNumber: CULong;
22	/// file handle for random number source
23	randomReader: file of CULong;
24	/// keeps GMP random number generator state
25	prngState: randState_T;
26	/// some arbitrary number
27	i: MPZ_T;
28begin
29	MP_randInit_MT(prngState);
30	assign(randomReader, randomFileName);
31	try
32		reset(randomReader);
33		try
34			try
35				read(randomReader, randomNumber);
36			except on eInOutError do
37			begin
38				randomize;
39				randomNumber := random(high(int64));
40			end;
41			end;
42		finally
43			close(randomReader);
44		end;
45		MP_randSeed_UI(prngState, randomNumber);
46		
47		MPZ_init(i);
48		try
49			MPZ_uRandomB(i, prngState, 256);
50			MP_printF('%Zd' + lineEnding, @i);
51		finally
52			MPZ_clear(i);
53		end;
54	finally	
55		MP_randClear(prngState);
56	end;
57end.

see also