Dev random

From Free Pascal wiki

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 *)
11 procedure randomize;
12 const
13 	/// file name for random(4) device
14 	randomDeviceName = '/dev/random';
15 var
16 	/// reading buffer
17 	// same type as system.randSeed
18 	randomNumber: cardinal;
19 	/// file handle
20 	randomReader: file of cardinal;
21 begin
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;
53 end;
54 {$else}
55 {$hint program does not use randomize based on /dev/random}
56 {$endif}

When using gmp, the same applies.

 1 program gmpRandomDemo(input, output, stderr);
 2 
 3 // objFPC mode for try..finally-construct
 4 {$mode objFPC}
 5 {$typedAddress on}
 6 
 7 uses
 8 	// familiarze with C types
 9 	cTypes,
10 	// familiarize with exception classes
11 	sysUtils,
12 	// use GNU multiple precision arithmetic library
13 	gmp;
14 
15 const
16 	/// file name for random(4) device
17 	randomFileName = '/dev/random';
18 
19 var
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;
28 begin
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;
57 end.

multiple values

Multiple random values can be read by utilizing system.blockRead:

 1 program multipleRandomValuesDemo(input, output, stderr);
 2 
 3 // include objpas unit
 4 {$mode objFPC}
 5 
 6 uses
 7 	// familiarize with exception classes
 8 	sysUtils;
 9 
10 const
11 	/// file name for urandom(4) device
12 	randomDeviceName = '/dev/urandom';
13 
14 type
15 	/// base type for numbers in this program
16 	number = dWord;
17 
18 var
19 	/// file handle for random number source
20 	randomFile: file of number;
21 	/// an array of numbers
22 	population: packed array[0..5] of number;
23 	/// temporary iterator variable
24 	i: number;
25 
26 begin
27 	assignFile(randomFile, randomDeviceName);
28 	try
29 		reset(randomFile);
30 		try
31 			blockRead(randomFile, population, length(population));
32 		except on eInOutError do
33 			exitCode := 1;
34 		end;
35 	finally
36 		closeFile(randomFile);
37 	end;
38 	
39 	for i in population do
40 	begin
41 		writeLn(i:10);
42 	end;
43 end.

see also