Dev random

From Lazarus wiki
Revision as of 12:49, 28 September 2022 by Kai Burghardt (talk | contribs) (→‎seed: update Run‑Time Library source code link [Subversion → Git Laboratory])
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

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 “unlimited”. Theoretically there is no limit on how much data one may obtain /dev/urandom.

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 run-time 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 oneself => fully qualified identifier
42			system.randomize;
43		end;
44		
45		close(randomReader);
46	end
47	{$pop}
48	else
49	begin
50		// do not call oneself => fully qualified identifier
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	// familiarize with C types
 9	cTypes,
10	// familiarize with exception classes
11	sysUtils,
12	// use GNU multiple precision arithmetic 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.

multiple values

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

 1program multipleRandomValuesDemo(input, output, stdErr);
 2
 3// include objpas unit
 4{$mode objFPC}
 5
 6uses
 7	// familiarize with exception classes
 8	sysUtils;
 9
10const
11	/// file name for urandom(4) device
12	randomDeviceName = '/dev/urandom';
13
14type
15	/// base type for numbers in this program
16	number = dWord;
17
18var
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
26begin
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;
43end.

see also