Dev random

From Lazarus wiki
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.

{$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
	/// reading buffer
	// same type as system.randSeed
	randomNumber: cardinal;
	/// file handle
	randomReader: file of cardinal;
begin
	assign(randomReader, randomDeviceName);
	{$push}
	// turn off run-time error generation
	{$IOChecks off}
	reset(randomReader);
	
	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 oneself => fully qualified identifier
			system.randomize;
		end;
		
		close(randomReader);
	end
	{$pop}
	else
	begin
		// do not call oneself => fully qualified identifier
		system.randomize;
	end;
end;
{$else}
{$hint program does not use randomize based on /dev/random}
{$endif}

When using gmp, the same applies.

program gmpRandomDemo(input, output, stderr);

// objFPC mode for try..finally-construct
{$mode objFPC}
{$typedAddress on}

uses
	// familiarize with C types
	cTypes,
	// familiarize with exception classes
	sysUtils,
	// use GNU multiple precision arithmetic library
	gmp;

const
	/// file name for random(4) device
	randomFileName = '/dev/random';

var
	/// reading buffer
	randomNumber: CULong;
	/// 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
	MP_randInit_MT(prngState);
	assign(randomReader, randomFileName);
	try
		reset(randomReader);
		try
			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.

multiple values

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

program multipleRandomValuesDemo(input, output, stdErr);

// include objpas unit
{$mode objFPC}

uses
	// familiarize with exception classes
	sysUtils;

const
	/// file name for urandom(4) device
	randomDeviceName = '/dev/urandom';

type
	/// base type for numbers in this program
	number = dWord;

var
	/// file handle for random number source
	randomFile: file of number;
	/// an array of numbers
	population: packed array[0..5] of number;
	/// temporary iterator variable
	i: number;

begin
	assignFile(randomFile, randomDeviceName);
	try
		reset(randomFile);
		try
			blockRead(randomFile, population, length(population));
		except on eInOutError do
			exitCode := 1;
		end;
	finally
		closeFile(randomFile);
	end;
	
	for i in population do
	begin
		writeLn(i:10);
	end;
end.

see also