Browse Source

add BCryptGenRandom for Windows

- Since CryptGenRandom has since been deprecated by Microsoft in favour of BCryptGenRandom, I added support for the later as recommended by Microsoft for newer OSes (Vista and above) while leaving CryptGenRandom as fallback on older OSes (XP).
Ugochukwu Mmaduekwe 6 years ago
parent
commit
f60a283038

+ 105 - 0
CryptoLib/src/Math/Raw/ClpNat.pas

@@ -93,6 +93,17 @@ type
     class function AddWordTo(len: Int32; x: UInt32;
       const z: TCryptoLibUInt32Array; zOff: Int32): UInt32; overload; static;
 
+    class function CAdd(len, mask: Int32; const x, y, z: TCryptoLibUInt32Array)
+      : UInt32; static;
+
+    class procedure CMov(len, mask: Int32; const x: TCryptoLibUInt32Array;
+      xOff: Int32; const z: TCryptoLibUInt32Array; zOff: Int32);
+      overload; static;
+
+    class procedure CMov(len, mask: Int32; const x: TCryptoLibInt32Array;
+      xOff: Int32; const z: TCryptoLibInt32Array; zOff: Int32);
+      overload; static;
+
     class procedure Copy(len: Int32; const x, z: TCryptoLibUInt32Array);
       overload; static; inline;
 
@@ -160,6 +171,12 @@ type
       const y: TCryptoLibUInt32Array; yOff, yLen: Int32;
       const zz: TCryptoLibUInt32Array; zzOff: Int32); overload; static;
 
+    class function MulAddTo(len: Int32; const x, y, zz: TCryptoLibUInt32Array)
+      : UInt32; overload; static;
+    class function MulAddTo(len: Int32; const x: TCryptoLibUInt32Array;
+      xOff: Int32; const y: TCryptoLibUInt32Array; yOff: Int32;
+      const zz: TCryptoLibUInt32Array; zzOff: Int32): UInt32; overload; static;
+
     class function Mul31BothAdd(len: Int32; a: UInt32;
       const x: TCryptoLibUInt32Array; b: UInt32;
       const y, z: TCryptoLibUInt32Array; zOff: Int32): UInt32; static;
@@ -874,6 +891,58 @@ begin
 
 end;
 
+class function TNat.CAdd(len, mask: Int32;
+  const x, y, z: TCryptoLibUInt32Array): UInt32;
+var
+  LMASK: UInt32;
+  c: UInt64;
+  I: Int32;
+begin
+  LMASK := UInt32(-(mask and 1));
+
+  c := 0;
+  for I := 0 to System.Pred(len) do
+  begin
+    c := c + (UInt64(x[I]) + (y[I] and LMASK));
+    z[I] := UInt32(c);
+    c := c shr 32;
+  end;
+  Result := UInt32(c);
+end;
+
+class procedure TNat.CMov(len, mask: Int32; const x: TCryptoLibUInt32Array;
+  xOff: Int32; const z: TCryptoLibUInt32Array; zOff: Int32);
+var
+  LMASK, z_i, diff: UInt32;
+  I: Int32;
+begin
+  LMASK := UInt32(-(mask and 1));
+
+  for I := 0 to System.Pred(len) do
+  begin
+    z_i := z[zOff + I];
+    diff := z_i xor x[xOff + I];
+    z_i := z_i xor ((diff and LMASK));
+    z[zOff + I] := z_i;
+  end;
+end;
+
+class procedure TNat.CMov(len, mask: Int32; const x: TCryptoLibInt32Array;
+  xOff: Int32; const z: TCryptoLibInt32Array; zOff: Int32);
+var
+  z_i, diff, I: Int32;
+begin
+  mask := -(mask and 1);
+
+  for I := 0 to System.Pred(len) do
+  begin
+    z_i := z[zOff + I];
+    diff := z_i xor x[xOff + I];
+    z_i := z_i xor ((diff and mask));
+    z[zOff + I] := z_i;
+  end;
+end;
+
 class function TNat.Copy(len: Int32; const x: TCryptoLibUInt32Array)
   : TCryptoLibUInt32Array;
 begin
@@ -1196,6 +1265,42 @@ begin
   end;
 end;
 
+class function TNat.MulAddTo(len: Int32;
+  const x, y, zz: TCryptoLibUInt32Array): UInt32;
+var
+  zc, c: UInt64;
+  I: Int32;
+begin
+  zc := 0;
+  for I := 0 to System.Pred(len) do
+  begin
+    c := MulWordAddTo(len, x[I], y, 0, zz, I) and M;
+    c := c + (zc + (zz[I + len] and M));
+    zz[I + len] := UInt32(c);
+    zc := c shr 32;
+  end;
+  Result := UInt32(zc);
+end;
+
+class function TNat.MulAddTo(len: Int32; const x: TCryptoLibUInt32Array;
+  xOff: Int32; const y: TCryptoLibUInt32Array; yOff: Int32;
+  const zz: TCryptoLibUInt32Array; zzOff: Int32): UInt32;
+var
+  zc, c: UInt64;
+  I: Int32;
+begin
+  zc := 0;
+  for I := 0 to System.Pred(len) do
+  begin
+    c := MulWordAddTo(len, x[xOff + I], y, yOff, zz, zzOff) and M;
+    c := c + (zc + (zz[zzOff + len] and M));
+    zz[zzOff + len] := UInt32(c);
+    zc := c shr 32;
+    System.Inc(zzOff);
+  end;
+  Result := UInt32(zc);
+end;
+
 class function TNat.Mul31BothAdd(len: Int32; a: UInt32;
   const x: TCryptoLibUInt32Array; b: UInt32; const y, z: TCryptoLibUInt32Array;
   zOff: Int32): UInt32;

+ 12 - 4
CryptoLib/src/Utils/ClpCryptoLibTypes.pas

@@ -208,14 +208,22 @@ type
 {$ENDIF DELPHIXE_UP}
 
   TCustomArrayBuffer<T> = record
+  private
+    FData: TCryptoLibGenericArray<T>;
+    FLength: Int32;
+    FIsNil: Boolean;
+
   public
-    Data: TCryptoLibGenericArray<T>;
-    Length: Int32;
-    IsNil: Boolean;
+    property Data: TCryptoLibGenericArray<T> read FData;
+    property Length: Int32 read FLength;
+    property IsNil: Boolean read FIsNil;
   end;
 
+  TCustomByteArrayBuffer = TCustomArrayBuffer<Byte>;
+
 const
-  EmptyBytesNotNil: TCustomArrayBuffer<Byte> = (Data: Nil; Length: 0; IsNil: False);
+  EmptyBytesNotNil: TCustomByteArrayBuffer = (FData: Nil; FLength: 0;
+    FIsNil: False);
 
 implementation
 

+ 135 - 20
CryptoLib/src/Utils/Randoms/ClpOSRandom.pas

@@ -24,6 +24,7 @@ interface
 uses
 {$IF DEFINED(MSWINDOWS)}
   Windows,
+  SysUtils,
 {$ELSEIF DEFINED(IOSDELPHI)}
   // iOS stuffs for Delphi
   Macapi.Dispatch,
@@ -39,8 +40,8 @@ uses
 
 resourcestring
 {$IF DEFINED(MSWINDOWS)}
-  SMSWIndowsCryptoAPIGenerationError =
-    'An Error Occured while generating random data using MS WIndows Crypto API.';
+  SMSWIndowsCryptographyAPIGenerationError =
+    'An Error Occured while generating random data using MS WIndows Cryptography API.';
 {$ELSEIF (DEFINED(IOSDELPHI) OR DEFINED(IOSFPC))}
   SIOSSecRandomCopyBytesGenerationError =
     'An Error Occured while generating random data using SecRandomCopyBytes API.';
@@ -75,15 +76,47 @@ type
       static; inline;
 
 {$IF DEFINED(MSWINDOWS)}
-    class function GenRandomBytesWindows(len: Int32; const data: PByte): Int32;
+
+  type
+    BCRYPT_ALG_HANDLE = THandle;
+    NTStatus = HRESULT;
+
+    TBCryptGenRandom = function(hAlgorithm: BCRYPT_ALG_HANDLE; pbBuffer: PUCHAR;
+      cbBuffer, dwFlags: ULONG): NTStatus; stdcall;
+
+    TBCryptOpenAlgorithmProvider = function(phAlgorithm: PVOID;
+      pszAlgId, pszImplementation: LPCWSTR; dwFlags: ULONG): NTStatus; stdcall;
+
+    TBCryptCloseAlgorithmProvider = function(hAlgorithm: BCRYPT_ALG_HANDLE;
+      dwFlags: ULONG): NTStatus; stdcall;
+
+  class var
+
+    FIsCngBCryptGenRandomSupportedOnOS: Boolean;
+    FBCryptGenRandom: TBCryptGenRandom;
+    FBCryptOpenAlgorithmProvider: TBCryptOpenAlgorithmProvider;
+    FBCryptCloseAlgorithmProvider: TBCryptCloseAlgorithmProvider;
+
+    class function GetIsCngBCryptGenRandomSupportedOnOS(): Boolean;
+      static; inline;
+
+    class function IsCngBCryptGenRandomAvailable(): Boolean; static;
+    class function GenRandomBytesWindows(len: Int32; const data: PByte)
+      : Int32; static;
+    class property IsCngBCryptGenRandomSupportedOnOS: Boolean
+      read GetIsCngBCryptGenRandomSupportedOnOS;
 {$ELSEIF DEFINED(IOSDELPHI)}
-    class function GenRandomBytesIOSDelphi(len: Int32;
-      const data: PByte): Int32;
+    class function GenRandomBytesIOSDelphi(len: Int32; const data: PByte)
+      : Int32; static;
 {$ELSEIF DEFINED(IOSFPC)}
-    class function GenRandomBytesIOSFPC(len: Int32; const data: PByte): Int32;
+    class function GenRandomBytesIOSFPC(len: Int32; const data: PByte)
+      : Int32; static;
 {$ELSE}
-    class function GenRandomBytesUnix(len: Int32; const data: PByte): Int32;
+    class function GenRandomBytesUnix(len: Int32; const data: PByte)
+      : Int32; static;
 {$IFEND $MSWINDOWS}
+    class procedure Boot(); static;
+    class constructor OSRandom();
   public
 
     class procedure GetBytes(const data: TCryptoLibByteArray); static;
@@ -123,8 +156,10 @@ function SecRandomCopyBytes(rnd: SecRandomRef; count: LongWord; bytes: PByte)
 
 type
   // similar to a TOpaqueData already defined in newer FPC but not available in 3.0.4
-  __SecRandom = record end;
- // similar to an OpaquePointer already defined in newer FPC but not available in 3.0.4
+  __SecRandom = record
+  end;
+
+  // similar to an OpaquePointer already defined in newer FPC but not available in 3.0.4
   SecRandomRef = ^__SecRandom;
 
 const
@@ -139,6 +174,18 @@ function SecRandomCopyBytes(rnd: SecRandomRef; count: LongWord; bytes: PByte)
 
 implementation
 
+class procedure TOSRandom.Boot;
+begin
+{$IFDEF MSWINDOWS}
+  FIsCngBCryptGenRandomSupportedOnOS := IsCngBCryptGenRandomAvailable();
+{$ENDIF MSWINDOWS}
+end;
+
+class constructor TOSRandom.OSRandom;
+begin
+  TOSRandom.Boot();
+end;
+
 {$IFDEF IOSDELPHI}
 
 function kSecRandomDefault: Pointer;
@@ -165,32 +212,100 @@ end;
 
 {$IF DEFINED(MSWINDOWS)}
 
+class function TOSRandom.GetIsCngBCryptGenRandomSupportedOnOS(): Boolean;
+begin
+  result := FIsCngBCryptGenRandomSupportedOnOS;
+end;
+
+class function TOSRandom.IsCngBCryptGenRandomAvailable(): Boolean;
+const
+  BCRYPT = 'bcrypt.dll';
+var
+  ModuleHandle: THandle;
+
+  function GetProcedureAddress(const AProcedureName: String;
+    var AFunctionFound: Boolean): Pointer;
+  begin
+    result := GetProcAddress(ModuleHandle, PChar(AProcedureName));
+    if result = Nil then
+    begin
+      AFunctionFound := False;
+    end;
+  end;
+
+begin
+  result := False;
+  ModuleHandle := SafeLoadLibrary(PChar(BCRYPT), SEM_FAILCRITICALERRORS);
+  if ModuleHandle <> 0 then
+  begin
+    result := True;
+    FBCryptOpenAlgorithmProvider :=
+      GetProcedureAddress('BCryptOpenAlgorithmProvider', result);
+    FBCryptCloseAlgorithmProvider :=
+      GetProcedureAddress('BCryptCloseAlgorithmProvider', result);
+    FBCryptGenRandom := GetProcedureAddress('BCryptGenRandom', result);
+  end;
+end;
+
 class function TOSRandom.GenRandomBytesWindows(len: Int32;
   const data: PByte): Int32;
+
+  function BCRYPT_SUCCESS(AStatus: NTStatus): Boolean; inline;
+  begin
+    result := AStatus >= 0;
+  end;
+
 var
   hProv: THandle;
 const
   PROV_RSA_FULL = 1;
   CRYPT_VERIFYCONTEXT = DWORD($F0000000);
   CRYPT_SILENT = $00000040;
+  // BCryptOpenAlgorithmProvider.AlgorithmID
+  BCRYPT_RNG_ALGORITHM: WideString = 'RNG';
+
 begin
-  if not CryptAcquireContextW(@hProv, nil, nil, PROV_RSA_FULL,
-    CRYPT_VERIFYCONTEXT or CRYPT_SILENT) then
+  if IsCngBCryptGenRandomSupportedOnOS then
   begin
-    result := HResultFromWin32(GetLastError);
-    Exit;
-  end;
+    // Windows Vista and Above
+    if (not BCRYPT_SUCCESS(FBCryptOpenAlgorithmProvider(@hProv,
+      PWideChar(BCRYPT_RNG_ALGORITHM), nil, 0))) then
+    begin
+      result := HResultFromWin32(GetLastError);
+      Exit;
+    end;
 
-  try
-    if not CryptGenRandom(hProv, len, data) then
+    try
+      if (not BCRYPT_SUCCESS(FBCryptGenRandom(hProv, PUCHAR(data),
+        LongWord(len), 0))) then
+      begin
+        result := HResultFromWin32(GetLastError);
+        Exit;
+      end;
+    finally
+      FBCryptCloseAlgorithmProvider(hProv, 0);
+    end;
+  end
+  else
+  begin
+    // Below Windows Vista
+    if not CryptAcquireContextW(@hProv, nil, nil, PROV_RSA_FULL,
+      CRYPT_VERIFYCONTEXT or CRYPT_SILENT) then
     begin
       result := HResultFromWin32(GetLastError);
       Exit;
     end;
-  finally
-    CryptReleaseContext(hProv, 0);
-  end;
 
+    try
+      if not CryptGenRandom(hProv, len, data) then
+      begin
+        result := HResultFromWin32(GetLastError);
+        Exit;
+      end;
+    finally
+      CryptReleaseContext(hProv, 0);
+    end;
+  end;
   result := S_OK;
 end;
 
@@ -254,7 +369,7 @@ begin
   if GenRandomBytesWindows(count, PByte(data)) <> 0 then
   begin
     raise EAccessCryptoLibException.CreateRes
-      (@SMSWIndowsCryptoAPIGenerationError);
+      (@SMSWIndowsCryptographyAPIGenerationError);
   end;
 
 {$ELSEIF DEFINED(IOSDELPHI)}

+ 20 - 0
CryptoLib/src/Utils/Rng/ClpRandomNumberGenerator.pas

@@ -29,11 +29,17 @@ uses
 
 resourcestring
   SUnknownAlgorithm = 'Unknown Random Generation Algorithm Requested';
+  SRandomNumberGeneratorOutputBufferNil =
+    'Random Number Generator Output Buffer Cannot Be Nil';
 
 type
   TRandomNumberGenerator = class abstract(TInterfacedObject,
     IRandomNumberGenerator)
 
+  strict protected
+    class procedure ValidateOutputBufferNotNull(const ABuffer
+      : TCryptoLibByteArray); static; inline;
+
   public
 
     type
@@ -81,6 +87,16 @@ implementation
 
 { TRandomNumberGenerator }
 
+class procedure TRandomNumberGenerator.ValidateOutputBufferNotNull
+  (const ABuffer: TCryptoLibByteArray);
+begin
+  if ABuffer = Nil then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes
+      (@SRandomNumberGeneratorOutputBufferNil);
+  end;
+end;
+
 class function TRandomNumberGenerator.CreateRNG: IRandomNumberGenerator;
 begin
   result := TRandomNumberGenerator.CreateRNG(TRandomNumberGeneratorMode.rngmOS);
@@ -121,12 +137,14 @@ end;
 
 procedure TOSRandomNumberGenerator.GetBytes(const data: TCryptoLibByteArray);
 begin
+  ValidateOutputBufferNotNull(data);
   TOSRandom.GetBytes(data);
 end;
 
 procedure TOSRandomNumberGenerator.GetNonZeroBytes
   (const data: TCryptoLibByteArray);
 begin
+  ValidateOutputBufferNotNull(data);
   TOSRandom.GetNonZeroBytes(data);
 end;
 
@@ -141,6 +159,7 @@ procedure TPCGRandomNumberGenerator.GetBytes(const data: TCryptoLibByteArray);
 var
   i: Int64;
 begin
+  ValidateOutputBufferNotNull(data);
   i := System.Length(data);
   while i > 0 do
   begin
@@ -156,6 +175,7 @@ var
   i: Int64;
   val: Byte;
 begin
+  ValidateOutputBufferNotNull(data);
   i := System.Length(data);
   while i > 0 do
   begin