Some notes about the current status of cspring randoms:
1. If you are on Linux, please note that if your kernel version is 4.8 or higher you can simply read /dev/urandom (not random, but
urandom) for cryptographically secure random data.
2. Unless you still need to support XP and older, on Windows you should use the bcrypt api and NOT the commonly used older crypto api.
Both are much faster and more secure than running through hoops like the examples we had to give in the olden days.
For the people who do want to look stupid by arguing otherwise: yes, urandom is cspring for modern kernels. It works such that the entropy pool is already filled before the startup of the system is completed.
The bcrypt API in Windows is preferred over the old crypto api for years, but a good satisfactory translation is not yet available. I have some small examples:
First bcrypt unit:
unit BCrypt;
interface
{ this partial bcrypt translation covers only the cryptographically
secure pseudo random generator (cspring) part }
uses
Windows;
const
{ comes as 32 bit and 64 bit, Intel, arm and aarch64,
depending on windows platform }
BCRYPT_LIB = 'bcrypt.dll';
// NTSTATUS values
STATUS_SUCCESS = $00000000;
// Algorithm Identifiers
BCRYPT_RNG_ALGORITHM = 'RNG';
BCRYPT_RNG_FIPS186_DSA_ALGORITHM = 'FIPS186DSARNG';
BCRYPT_RNG_DUAL_EC_ALGORITHM = 'DUALECRNG';
// Flags
BCRYPT_RNG_USE_ENTROPY_IN_BUFFER = $00000001;
BCRYPT_USE_SYSTEM_PREFERRED_RNG = $00000002;
type
BCRYPT_ALG_HANDLE = THandle;
PBCRYPT_ALG_HANDLE = ^BCRYPT_ALG_HANDLE;
NTSTATUS = LongInt;
// Function prototypes
function BCryptOpenAlgorithmProvider(
phAlgorithm: PBCRYPT_ALG_HANDLE;
pszAlgId: LPCWSTR;
pszImplementation: LPCWSTR;
dwFlags: DWORD
): NTSTATUS; winapi; external BCRYPT_LIB;
function BCryptCloseAlgorithmProvider(
hAlgorithm: BCRYPT_ALG_HANDLE;
dwFlags: DWORD
): NTSTATUS; winapi; external BCRYPT_LIB;
function BCryptGenRandom(
hAlgorithm: BCRYPT_ALG_HANDLE;
pbBuffer: PUCHAR;
cbBuffer: ULONG;
dwFlags: DWORD
): NTSTATUS; winapi; external BCRYPT_LIB;
implementation
end.
Second a cross-platform cspring:
unit uCspring;
{$mode objfpc}
interface
{ cross-platform cspring }
function SystemCSPRNG(var Buffer; Bytes: LongWord): Boolean;inline;
implementation
uses
{$ifdef mswindows}windows,bcrypt;{$endif}
{$ifdef UNIX}cthreads;{$endif}
{$ifdef mswindows}
function WinCSPRNG(var Buffer; Bytes: LongWord): Boolean;
var
hAlgorithm: PBCRYPT_ALG_HANDLE;
begin
Result := False;
if BCryptOpenAlgorithmProvider(@hAlgorithm, BCRYPT_RNG_ALGORITHM, nil, 0) = STATUS_SUCCESS then
try
if BCryptGenRandom(PtrUint(hAlgorithm), @Buffer, Bytes, 0) = STATUS_SUCCESS then
Result := True;
finally
BCryptCloseAlgorithmProvider(ptrUint(hAlgorithm), 0);
end;
end;
(*
{ this is for XP, that does not have bcrypt, change windows to jwaWindows. }
function WinLegacyCSPRNG(var Buffer; Count: DWORD): Boolean;
var
hProv: HCRYPTPROV;
begin
Result := CryptAcquireContext(hProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
if Result then
try
Result := CryptGenRandom(hProv, Count, @Buffer);
finally
CryptReleaseContext(hProv, 0);
end;
end;
*)
{$else}
{ The Linux CSPRING can simply be read from /dev/urandom on modern
kernels >= 4.8 and is cryptographically secure. Entropy is determined
during system startup and so directly available when startup is finished.
Older kernels, though, used a simpler less secure PRNG, without taking
enough entropy from the hardware }
function UnixCSPRNG(var Buffer; Bytes: LongWord): Boolean;
var
f: File of byte;
begin
Result := False;
Assign(f, '/dev/urandom');
Reset(f, 1);
try
BlockRead(f, Buffer, Bytes);
Result := True;
finally
Close(f);
end;
end;
{$endif}
function SystemCSPRNG(var Buffer; Bytes: LongWord): Boolean;
begin
{$IFDEF WINDOWS}
Result := WinCSPRNG(Buffer, Bytes);
{$ENDIF}
{$IFDEF UNIX }
Result := UnixCSPRNG(Buffer, Bytes);
{$ENDIF}
end;
end.
This code completely bypasses any other random number generators and is also much, much faster.
Demo:
program cspringdemo;
{ demo for various sizes of cspring random numbers, signed and unsigned. }
{$mode objfpc}
uses ucspring;
var
// unsigned
Buf8 : array[0..9] of byte;
Buf16: array[0..9] of word;
Buf32: array[0..9] of dword;
Buf64: array[0..9] of qword;
// signed
Buf8s : array[0..9] of ShortInt;
Buf16s: array[0..9] of SmallInt;
Buf32s: array[0..9] of integer;
Buf64s: array[0..9] of int64;
b:ShortInt;
c:SmallInt;
d:Integer;
e:Int64;
f:byte;
g:word;
h:dword;
i:qword;
begin
if SystemCSPRNG(Buf8s,length(Buf8s)) then
for b in Buf8s do write(b:5);
writeln;
if SystemCSPRNG(Buf16s,length(Buf16s) * 2) then
for c in Buf16s do write(c:10);
writeln;
if SystemCSPRNG(Buf32s,length(Buf32s) * 4) then
for d in Buf32s do write(d:20);
writeln;
if SystemCSPRNG(Buf64s,length(Buf64s) * 8) then
for e in Buf64s do write(e:40);
writeln;
if SystemCSPRNG(Buf8,length(Buf8)) then
for f in Buf8 do write(f:5);
writeln;
if SystemCSPRNG(Buf16,length(Buf16) * 2) then
for g in Buf16 do write(g:10);
writeln;
if SystemCSPRNG(Buf32,length(Buf32) * 4) then
for h in Buf32 do write(h:20);
writeln;
if SystemCSPRNG(Buf64,length(Buf64) * 8) then
for i in Buf64 do write(i:40);
writeln;
end.