jeudi 5 janvier 2023

Delphi - use Windows OS CryptGenRandomBytes to generate random bytes

I read Are there any cryptographically secure PRNG libraries for Delphi? but the solution it presents uses WCrypt2 which I don't want to use.

So I made my own unit depending on nothing.

An example using this unit:

uses SysUtils, WinRandomBytes;

function BytesToHex(const Bytes: TBytes): string;
var i : integer;
begin
    Result := '';

    for i := 0 to Length(Bytes)-1 do
    begin
        Result := Result + IntToHex(Bytes[i],2);
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var bytes : TBytes;
begin
    bytes := OSWinRandomBytes(32);

    ShowMessage(BytesToHex(bytes));
end;

This is the new unit for acquiring random bytes via the Windows Crypto API. It exposes one function OSWinRandomBytes() which either returns the requested random bytes or raises an error.

unit WinRandomBytes;

interface
uses SysUtils;

{ Return requested TBytes or raise an Exception }
function OSWinRandomBytes(Length: cardinal) : TBytes;

implementation
uses Windows;

type HCRYPTPROV = pointer;
type TCryptAcquireContextA = function(var phProv: HCRYPTPROV; pszContainer: PAnsiChar; pszProvider: PAnsiChar; dwProvType: DWORD; dwFlags: DWORD): BOOL; stdcall;
type TCryptGenRandom = function(hProv: HCRYPTPROV; dwLen: DWORD; pbBuffer: Pointer): BOOL; stdcall;
type TCryptReleaseContext = function(hProv: HCRYPTPROV; dwFlags: DWORD): BOOL; stdcall;

const PROV_RSA_FULL = 1;
const CRYPT_VERIFYCONTEXT = $F0000000;

var AdvApiLoaded: boolean;
var AdvApiHandle: HModule;
var CryptAcquireContextA : TCryptAcquireContextA;
var CryptGenRandom : TCryptGenRandom;
var CryptReleaseContext : TCryptReleaseContext;

function LoadAdvApi() : boolean;
begin
    if not AdvApiLoaded then
        begin
            AdvApiLoaded := true;

            AdvApiHandle := LoadLibrary('advapi32.dll');
            if AdvApiHandle <> 0 then
                begin
                    CryptAcquireContextA := GetProcAddress(AdvApiHandle,'CryptAcquireContextA');
                    CryptGenRandom := GetProcAddress(AdvApiHandle,'CryptGenRandom');
                    CryptReleaseContext := GetProcAddress(AdvApiHandle,'CryptReleaseContext');
                end;
        end;

    Result := Assigned(CryptAcquireContextA) and Assigned(CryptGenRandom) and Assigned(CryptReleaseContext);
end;

function OSWinRandomBytes(Length: cardinal) : TBytes;
var hProv : HCRYPTPROV;
begin
    if not LoadAdvApi() then
        begin
            raise Exception.Create('Error loading advapi32.dll');
        end;

    hProv := nil;
    if not CryptAcquireContextA(hProv,
                                nil,
                                nil,
                                PROV_RSA_FULL,
                                CRYPT_VERIFYCONTEXT) then
        begin
            RaiseLastOSError();
        end;

    try
        SetLength(Result, Length);

        if not CryptGenRandom(hProv, Length, @Result[0]) then
            begin
                RaiseLastOSError();
            end;
    finally
        CryptReleaseContext(hProv, 0);
    end;
end;

begin
    AdvApiLoaded := false;
    AdvApiHandle := 0;
    CryptAcquireContextA := nil;
    CryptGenRandom := nil;
    CryptReleaseContext := nil;
end.



Aucun commentaire:

Enregistrer un commentaire