Browse Source

major refactoring of OSRandom class for strict OS compliance

Ugochukwu Mmaduekwe 6 years ago
parent
commit
07295d0b41

+ 1 - 1
CryptoLib.Tests/src/Math/EC/Rfc7748/X25519Tests.pas

@@ -278,7 +278,7 @@ initialization
 // Register any test cases with the test runner
 // Register any test cases with the test runner
 
 
 {$IFDEF FPC}
 {$IFDEF FPC}
-// RegisterTest(TTestX25519);
+ RegisterTest(TTestX25519);
 {$ELSE}
 {$ELSE}
   RegisterTest(TTestX25519.Suite);
   RegisterTest(TTestX25519.Suite);
 {$ENDIF FPC}
 {$ENDIF FPC}

+ 1 - 1
CryptoLib.Tests/src/Math/EC/Rfc8032/Ed25519Tests.pas

@@ -538,7 +538,7 @@ initialization
 // Register any test cases with the test runner
 // Register any test cases with the test runner
 
 
 {$IFDEF FPC}
 {$IFDEF FPC}
- // RegisterTest(TTestEd25519);
+  RegisterTest(TTestEd25519);
 {$ELSE}
 {$ELSE}
   RegisterTest(TTestEd25519.Suite);
   RegisterTest(TTestEd25519.Suite);
 {$ENDIF FPC}
 {$ENDIF FPC}

+ 1 - 1
CryptoLib.Tests/src/Others/Ed25519HigherLevelTests.pas

@@ -421,7 +421,7 @@ initialization
 // Register any test cases with the test runner
 // Register any test cases with the test runner
 
 
 {$IFDEF FPC}
 {$IFDEF FPC}
-// RegisterTest(TTestEd25519HigherLevel);
+  RegisterTest(TTestEd25519HigherLevel);
 {$ELSE}
 {$ELSE}
   RegisterTest(TTestEd25519HigherLevel.Suite);
   RegisterTest(TTestEd25519HigherLevel.Suite);
 {$ENDIF FPC}
 {$ENDIF FPC}

+ 1 - 1
CryptoLib.Tests/src/Others/X25519HigherLevelTests.pas

@@ -128,7 +128,7 @@ initialization
 // Register any test cases with the test runner
 // Register any test cases with the test runner
 
 
 {$IFDEF FPC}
 {$IFDEF FPC}
-// RegisterTest(TTestX25519HigherLevel);
+ RegisterTest(TTestX25519HigherLevel);
 {$ELSE}
 {$ELSE}
   RegisterTest(TTestX25519HigherLevel.Suite);
   RegisterTest(TTestX25519HigherLevel.Suite);
 {$ENDIF FPC}
 {$ENDIF FPC}

+ 101 - 16
CryptoLib/src/Include/CryptoLib.inc

@@ -23,8 +23,48 @@
 {$UNDEF DELPHI}
 {$UNDEF DELPHI}
 {$MODE delphi}
 {$MODE delphi}
 
 
-{$IF (DEFINED(DARWIN) AND (DEFINED(CPUARM) OR DEFINED(CPUAARCH64) OR DEFINED(IPHONESIM)))}
-{$DEFINE IOSFPC}
+{$IFDEF CPU386}
+   {$DEFINE CRYPTOLIB_X86}
+{$ENDIF}
+
+{$IFDEF CPUX64}
+   {$DEFINE CRYPTOLIB_X86_64}
+{$ENDIF}
+
+{$IFDEF CPUARM}
+   {$DEFINE CRYPTOLIB_ARM}
+{$ENDIF}
+
+{$IFDEF CPUAARCH64}
+   {$DEFINE CRYPTOLIB_AARCH64}
+{$ENDIF}
+
+{$IFDEF IPHONESIM}
+   {$DEFINE CRYPTOLIB_IOSSIM}
+{$ENDIF}
+
+{$IF DEFINED(MSWINDOWS)}
+   {$DEFINE CRYPTOLIB_MSWINDOWS}
+{$ELSEIF DEFINED(UNIX)}
+   {$DEFINE CRYPTOLIB_UNIX}
+   {$IF DEFINED(BSD)}
+      {$IF DEFINED(DARWIN)}
+         {$DEFINE CRYPTOLIB_APPLE}
+         {$IF DEFINED(CRYPTOLIB_ARM) OR DEFINED(CRYPTOLIB_AARCH64)}
+            {$DEFINE CRYPTOLIB_IOS}
+         {$ELSE}
+            {$DEFINE CRYPTOLIB_MACOS}
+         {$IFEND}
+      {$ELSEIF DEFINED(FREEBSD) OR DEFINED(NETBSD) OR DEFINED(OPENBSD) OR DEFINED(DRAGONFLY)}
+         {$DEFINE CRYPTOLIB_GENERIC_BSD}
+      {$IFEND}
+  {$ELSEIF DEFINED(LINUX)}
+     {$DEFINE CRYPTOLIB_LINUX}
+  {$ELSE}
+     {$DEFINE CRYPTOLIB_UNDEFINED_UNIX_VARIANTS}
+  {$IFEND}
+{$ELSE}
+   {$MESSAGE ERROR 'UNSUPPORTED TARGET.'}
 {$ENDIF}
 {$ENDIF}
 
 
 {$DEFINE USE_UNROLLED_VARIANT}
 {$DEFINE USE_UNROLLED_VARIANT}
@@ -49,20 +89,65 @@
 {$OPTIMIZATION STRENGTH}
 {$OPTIMIZATION STRENGTH}
 {$OPTIMIZATION CSE}
 {$OPTIMIZATION CSE}
 {$OPTIMIZATION DFA}
 {$OPTIMIZATION DFA}
+
 {$IFDEF CPUI386}
 {$IFDEF CPUI386}
-{$OPTIMIZATION USEEBP}
+   {$OPTIMIZATION USEEBP}
 {$ENDIF}
 {$ENDIF}
+
 {$IFDEF CPUX86_64}
 {$IFDEF CPUX86_64}
-{$OPTIMIZATION USERBP}
+   {$OPTIMIZATION USERBP}
 {$ENDIF}
 {$ENDIF}
+
 {$ENDIF FPC}
 {$ENDIF FPC}
 
 
 (* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
 (* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
 
 
 {$IFDEF DELPHI}
 {$IFDEF DELPHI}
 
 
-{$IF DEFINED(IOS)}
-{$DEFINE IOSDELPHI}
+{$IFDEF CPU386}
+   {$DEFINE CRYPTOLIB_X86}
+{$ENDIF}
+
+{$IFDEF CPUX64}
+   {$DEFINE CRYPTOLIB_X86_64}
+{$ENDIF}
+
+{$IFDEF CPUARM32}
+   {$DEFINE CRYPTOLIB_ARM}
+{$ENDIF}
+
+{$IFDEF CPUARM64}
+   {$DEFINE CRYPTOLIB_AARCH64}
+{$ENDIF}
+
+{$IFDEF IOS}
+  {$IFNDEF CPUARM}
+     {$DEFINE CRYPTOLIB_IOSSIM}
+  {$ENDIF}
+{$ENDIF}
+
+{$IFDEF IOS}
+   {$DEFINE CRYPTOLIB_IOS}
+{$IFEND}
+
+{$IFDEF MSWINDOWS}
+   {$DEFINE CRYPTOLIB_MSWINDOWS}
+{$ENDIF}
+
+{$IFDEF MACOS}
+   {$DEFINE CRYPTOLIB_MACOS}
+{$ENDIF}
+
+{$IF DEFINED(CRYPTOLIB_IOS) OR DEFINED(CRYPTOLIB_MACOS)}
+   {$DEFINE CRYPTOLIB_APPLE}
+{$IFEND}
+
+{$IF DEFINED(LINUX) OR DEFINED(ANDROID)}
+   {$DEFINE CRYPTOLIB_LINUX}
+{$ENDIF}
+
+{$IF DEFINED(CRYPTOLIB_APPLE) OR DEFINED(CRYPTOLIB_LINUX)}
+   {$DEFINE CRYPTOLIB_UNIX}
 {$ENDIF}
 {$ENDIF}
 
 
 {$DEFINE USE_UNROLLED_VARIANT}
 {$DEFINE USE_UNROLLED_VARIANT}
@@ -89,32 +174,32 @@
 
 
  // XE3 and Above
  // XE3 and Above
 {$IF CompilerVersion >= 24.0}
 {$IF CompilerVersion >= 24.0}
-{$DEFINE DELPHIXE3_UP}
-{$DEFINE SUPPORT_TSTREAM_READ_BYTEARRAY_OVERLOAD}
-{$DEFINE SUPPORT_TSTREAM_WRITE_BYTEARRAY_OVERLOAD}
-{$LEGACYIFEND ON}
-{$ZEROBASEDSTRINGS OFF}
+   {$DEFINE DELPHIXE3_UP}
+   {$DEFINE SUPPORT_TSTREAM_READ_BYTEARRAY_OVERLOAD}
+   {$DEFINE SUPPORT_TSTREAM_WRITE_BYTEARRAY_OVERLOAD}
+   {$LEGACYIFEND ON}
+   {$ZEROBASEDSTRINGS OFF}
 {$IFEND}
 {$IFEND}
 
 
   // XE and Above
   // XE and Above
 {$IF CompilerVersion >= 22.0}
 {$IF CompilerVersion >= 22.0}
-{$DEFINE DELPHIXE_UP}
+   {$DEFINE DELPHIXE_UP}
 {$IFEND}
 {$IFEND}
 
 
  // XE4 and Above
  // XE4 and Above
 {$IF CompilerVersion >= 25.0}
 {$IF CompilerVersion >= 25.0}
-{$DEFINE DELPHIXE4_UP}
-{$DEFINE SHIFT_OVERFLOW_BUG_FIXED}
+   {$DEFINE DELPHIXE4_UP}
+   {$DEFINE SHIFT_OVERFLOW_BUG_FIXED}
 {$IFEND}
 {$IFEND}
 
 
   // 10.2 Tokyo and Above
   // 10.2 Tokyo and Above
 {$IF CompilerVersion >= 32.0}
 {$IF CompilerVersion >= 32.0}
-{$DEFINE DELPHI10.2_TOKYO_UP}
+   {$DEFINE DELPHI10.2_TOKYO_UP}
 {$IFEND}
 {$IFEND}
 
 
   // 10.2 Tokyo and Above
   // 10.2 Tokyo and Above
 {$IFNDEF DELPHI10.2_TOKYO_UP}
 {$IFNDEF DELPHI10.2_TOKYO_UP}
-{$MESSAGE ERROR 'This Library requires Delphi Tokyo or higher.'}
+   {$MESSAGE ERROR 'This Library requires Delphi Tokyo or higher.'}
 {$ENDIF}
 {$ENDIF}
 
 
 
 

+ 2 - 2
CryptoLib/src/Include/CryptoLibHelper.inc

@@ -17,10 +17,10 @@
 
 
 {$MACRO ON}
 {$MACRO ON}
 {$IFDEF ENDIAN_BIG}
 {$IFDEF ENDIAN_BIG}
-{$MESSAGE FATAL 'This Library does not support "Big Endian" processors yet.'}
+   {$MESSAGE FATAL 'This Library does not support "Big Endian" processors yet.'}
 {$ENDIF}
 {$ENDIF}
 // FPC 3.0.4 and Above
 // FPC 3.0.4 and Above
 // Had to Include this here since Delphi does not allow it Compile in "CryptoLib.inc".
 // Had to Include this here since Delphi does not allow it Compile in "CryptoLib.inc".
 {$IF FPC_FULLVERSION < 30004}
 {$IF FPC_FULLVERSION < 30004}
-{$MESSAGE ERROR 'This Library requires FreePascal 3.0.4 or higher.'}
+   {$MESSAGE ERROR 'This Library requires FreePascal 3.0.4 or higher.'}
 {$IFEND}
 {$IFEND}

+ 1 - 1
CryptoLib/src/Utils/ClpCryptoLibTypes.pas

@@ -53,7 +53,7 @@ type
   EInvalidCipherTextCryptoLibException = class(ECryptoLibException);
   EInvalidCipherTextCryptoLibException = class(ECryptoLibException);
   EStreamCryptoLibException = class(ECryptoLibException);
   EStreamCryptoLibException = class(ECryptoLibException);
   ESecurityUtilityCryptoLibException = class(ECryptoLibException);
   ESecurityUtilityCryptoLibException = class(ECryptoLibException);
-  EAccessCryptoLibException = class(ECryptoLibException);
+  EOSRandomCryptoLibException = class(ECryptoLibException);
   EDataLengthCryptoLibException = class(ECryptoLibException);
   EDataLengthCryptoLibException = class(ECryptoLibException);
   EMaxBytesExceededCryptoLibException = class(ECryptoLibException);
   EMaxBytesExceededCryptoLibException = class(ECryptoLibException);
   EOutputLengthCryptoLibException = class(ECryptoLibException);
   EOutputLengthCryptoLibException = class(ECryptoLibException);

+ 297 - 109
CryptoLib/src/Utils/Randoms/ClpOSRandom.pas

@@ -22,33 +22,65 @@ unit ClpOSRandom;
 interface
 interface
 
 
 uses
 uses
-{$IF DEFINED(MSWINDOWS)}
+{$IFDEF CRYPTOLIB_MSWINDOWS}
   Windows,
   Windows,
-  SysUtils,
-{$ELSEIF DEFINED(IOSDELPHI)}
-  // iOS stuffs for Delphi
-  Macapi.Dispatch,
-  iOSapi.Foundation,
-{$ELSEIF DEFINED(IOSFPC)}
-  // iOS stuffs for FreePascal
+{$ENDIF} // ENDIF CRYPTOLIB_MSWINDOWS
+{$IFDEF CRYPTOLIB_APPLE}
+{$IFDEF FPC}
 {$LINKFRAMEWORK Security}
 {$LINKFRAMEWORK Security}
 {$ELSE}
 {$ELSE}
+  // Macapi.Dispatch, or
+  Macapi.ObjCRuntime,
+{$IF DEFINED(CRYPTOLIB_IOS)}
+  iOSapi.Foundation,
+{$ELSEIF DEFINED(CRYPTOLIB_MACOS)}
+  Macapi.Foundation,
+{$ELSE}
+{$MESSAGE ERROR 'UNSUPPORTED TARGET.'}
+{$IFEND} // ENDIF CRYPTOLIB_MACOS
+{$ENDIF}  // ENDIF FPC
+{$ENDIF}   // ENDIF CRYPTOLIB_APPLE
+{$IFDEF CRYPTOLIB_LINUX}
+{$IFDEF FPC}
+  BaseUnix,
+  dl,
+{$ELSE}
+  Posix.Errno,
+  Posix.Dlfcn,
+{$ENDIF}
+{$ENDIF}  // ENDIF CRYPTOLIB_LINUX
+{$IFDEF CRYPTOLIB_PUREBSD}
+  // PureBSD (NetBSD, FreeBSD, OpenBSD)
+{$ENDIF}  // ENDIF CRYPTOLIB_PUREBSD
+{$IFDEF CRYPTOLIB_UNIX}
   Classes,
   Classes,
+{$ENDIF}  // ENDIF CRYPTOLIB_UNIX
+{$IF DEFINED(CRYPTOLIB_MSWINDOWS) OR DEFINED(CRYPTOLIB_UNIX)}
   SysUtils,
   SysUtils,
-{$IFEND MSWINDOWS}
+{$IFEND}  // ENDIF CRYPTOLIB_MSWINDOWS OR CRYPTOLIB_UNIX
   ClpCryptoLibTypes;
   ClpCryptoLibTypes;
 
 
 resourcestring
 resourcestring
-{$IF DEFINED(MSWINDOWS)}
+{$IFDEF CRYPTOLIB_MSWINDOWS}
   SMSWIndowsCryptographyAPIGenerationError =
   SMSWIndowsCryptographyAPIGenerationError =
     'An Error Occured while generating random data using MS WIndows Cryptography API.';
     'An Error Occured while generating random data using MS WIndows Cryptography API.';
-{$ELSEIF (DEFINED(IOSDELPHI) OR DEFINED(IOSFPC))}
-  SIOSSecRandomCopyBytesGenerationError =
+{$ENDIF}
+{$IFDEF CRYPTOLIB_APPLE}
+  SAppleSecRandomCopyBytesGenerationError =
     'An Error Occured while generating random data using SecRandomCopyBytes API.';
     'An Error Occured while generating random data using SecRandomCopyBytes API.';
-{$ELSE}
-  SUnixRandomReadError =
-    'An Error Occured while reading random data from /dev/urandom or /dev/random.';
-{$IFEND MSWINDOWS}
+{$ENDIF}
+{$IFDEF CRYPTOLIB_LINUX}
+  SLinuxGetRandomError =
+    'An Error Occured while generating random data using getRandom API';
+{$ENDIF}
+{$IFDEF CRYPTOLIB_GENERIC_BSD}
+  SArc4RandomBufGenerationError =
+    'An Error Occured while generating random data using getRandom API.';
+{$ENDIF}
+{$IFDEF CRYPTOLIB_UNIX}
+  SRandomDeviceReadError =
+    'An Error Occured while reading random data from random device (file)';
+{$ENDIF}
 
 
 type
 type
 
 
@@ -59,23 +91,83 @@ type
   /// <para>
   /// <para>
   /// This class returns random bytes from an OS-specific randomness
   /// This class returns random bytes from an OS-specific randomness
   /// source. The returned data should be unpredictable enough for
   /// source. The returned data should be unpredictable enough for
-  /// cryptographic applications, though its exact quality depends on the
-  /// OS implementation.
-  /// On a UNIX-like system this will read directly from /dev/urandom or /dev/random
-  /// (if the former is not available),
-  /// on iOS, calls SecRandomCopyBytes as /dev/(u)random is sandboxed,
-  /// on MSWINDOWS it will call CryptGenRandom().
+  /// cryptographic applications, though it's exact quality depends on
+  /// the OS implementation.
   /// </para>
   /// </para>
+  /// <list type="table">
+  /// <listheader>
+  /// <term>OS</term>
+  /// <description>Interface</description>
+  /// </listheader>
+  /// <item>
+  /// <term>Linux, Android</term>
+  /// <description><see href="http://man7.org/linux/man-pages/man2/getrandom.2.html">
+  /// getrandom</see> system call if available, otherwise ( <b>
+  /// /dev/urandom</b> or <b>/dev/random</b>) (which ever is
+  /// available)</description>
+  /// </item>
+  /// <item>
+  /// <term>Windows</term>
+  /// <description><see href="https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-cryptgenrandom">
+  /// CryptGenRandom</see> for <b>XP</b>, <see href="https://docs.microsoft.com/en-us/windows/desktop/api/bcrypt/nf-bcrypt-bcryptgenrandom">
+  /// BCryptGenRandom</see> for <b>Vista</b> Upwards</description>
+  /// </item>
+  /// <item>
+  /// <term>macOS, iOS</term>
+  /// <description><see href="https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc">
+  /// SecRandomCopyBytes</see><br /></description>
+  /// </item>
+  /// <item>
+  /// <term>FreeBSD</term>
+  /// <description><see href="https://www.freebsd.org/cgi/man.cgi?query=arc4random&amp;sektion=3&amp;manpath=FreeBSD+12.0-RELEASE+and+Ports">
+  /// arc4random_buf</see></description>
+  /// </item>
+  /// <item>
+  /// <term>NetBSD</term>
+  /// <description><see href="https://www.netbsd.org/~riastradh/tmp/20141116/arc4random.html">
+  /// arc4random_buf</see></description>
+  /// </item>
+  /// <item>
+  /// <term>OpenBSD</term>
+  /// <description><see href="http://man.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/arc4random.3">
+  /// arc4random_buf</see></description>
+  /// </item>
+  /// <item>
+  /// <term>DragonFly</term>
+  /// <description><see href="https://www.dragonflybsd.org/cgi/web-man?command=arc4random&amp;section=3">
+  /// arc4random_buf</see></description>
+  /// </item>
+  /// </list>
   /// </summary>
   /// </summary>
-
   TOSRandom = class sealed(TObject)
   TOSRandom = class sealed(TObject)
 
 
   strict private
   strict private
 
 
-    class function NoZeroes(const data: TCryptoLibByteArray): Boolean;
-      static; inline;
+    // ================================================================//
+
+{$IFDEF CRYPTOLIB_LINUX}
+  type
+    TGetRandom = function(pbBuffer: PByte; buflen: LongWord; flags: UInt32)
+      : Int32; cdecl;
+
+  class var
+
+    FIsGetRandomSupportedOnOS: Boolean;
+    FGetRandom: TGetRandom;
+
+    class function ErrorNo: Int32; static; inline;
 
 
-{$IF DEFINED(MSWINDOWS)}
+    class function GetIsGetRandomSupportedOnOS(): Boolean; static; inline;
+
+    class function IsGetRandomAvailable(): Boolean; static;
+
+    class property IsGetRandomSupportedOnOS: Boolean
+      read GetIsGetRandomSupportedOnOS;
+
+{$ENDIF}
+    // ================================================================//
+
+{$IFDEF CRYPTOLIB_MSWINDOWS}
 
 
   type
   type
     BCRYPT_ALG_HANDLE = THandle;
     BCRYPT_ALG_HANDLE = THandle;
@@ -101,30 +193,46 @@ type
       static; inline;
       static; inline;
 
 
     class function IsCngBCryptGenRandomAvailable(): Boolean; static;
     class function IsCngBCryptGenRandomAvailable(): Boolean; static;
-    class function GenRandomBytesWindows(len: Int32; const data: PByte)
+    class function GenRandomBytesWindows(len: Int32; data: PByte)
       : Int32; static;
       : Int32; static;
     class property IsCngBCryptGenRandomSupportedOnOS: Boolean
     class property IsCngBCryptGenRandomSupportedOnOS: Boolean
       read GetIsCngBCryptGenRandomSupportedOnOS;
       read GetIsCngBCryptGenRandomSupportedOnOS;
-{$ELSEIF DEFINED(IOSDELPHI)}
-    class function GenRandomBytesIOSDelphi(len: Int32; const data: PByte)
+{$ENDIF}
+
+    // ================================================================//
+{$IFDEF CRYPTOLIB_APPLE}
+    class function GenRandomBytesApple(len: Int32; data: PByte): Int32; static;
+{$ENDIF}
+    // ================================================================//
+{$IFDEF CRYPTOLIB_LINUX}
+    class function GenRandomBytesLinux(len: Int32; data: PByte): Int32; static;
+{$ENDIF}
+    // ================================================================//
+{$IFDEF CRYPTOLIB_GENERIC_BSD}
+    class function GenRandomBytesGenericBSD(len: Int32; data: PByte)
       : Int32; static;
       : Int32; static;
-{$ELSEIF DEFINED(IOSFPC)}
-    class function GenRandomBytesIOSFPC(len: Int32; const data: PByte)
+{$ENDIF}
+    // ================================================================//
+{$IFDEF CRYPTOLIB_UNIX}
+    class function dev_random_device_read(len: Int32; data: PByte)
       : Int32; static;
       : Int32; static;
-{$ELSE}
-    class function GenRandomBytesUnix(len: Int32; const data: PByte)
-      : Int32; static;
-{$IFEND $MSWINDOWS}
+{$ENDIF}
+    // ================================================================//
+
+    class function NoZeroes(const data: TCryptoLibByteArray): Boolean;
+      static; inline;
     class procedure Boot(); static;
     class procedure Boot(); static;
     class constructor OSRandom();
     class constructor OSRandom();
-  public
 
 
+  public
     class procedure GetBytes(const data: TCryptoLibByteArray); static;
     class procedure GetBytes(const data: TCryptoLibByteArray); static;
     class procedure GetNonZeroBytes(const data: TCryptoLibByteArray); static;
     class procedure GetNonZeroBytes(const data: TCryptoLibByteArray); static;
 
 
   end;
   end;
 
 
-{$IFDEF MSWINDOWS}
+  // ************************************************************************//
+
+{$IFDEF CRYPTOLIB_MSWINDOWS}
 
 
 const
 const
   ADVAPI32 = 'advapi32.dll';
   ADVAPI32 = 'advapi32.dll';
@@ -138,21 +246,10 @@ function CryptGenRandom(hProv: THandle; dwLen: DWORD; pbBuffer: PByte): BOOL;
 
 
 function CryptReleaseContext(hProv: THandle; dwFlags: DWORD): BOOL; stdcall;
 function CryptReleaseContext(hProv: THandle; dwFlags: DWORD): BOOL; stdcall;
   external ADVAPI32 Name 'CryptReleaseContext';
   external ADVAPI32 Name 'CryptReleaseContext';
-{$ENDIF MSWINDOWS}
-{$IFDEF IOSDELPHI}
-
-type
-  SecRandomRef = Pointer;
-
-const
-  libSecurity = '/System/Library/Frameworks/Security.framework/Security';
-
-function kSecRandomDefault: Pointer;
-
-function SecRandomCopyBytes(rnd: SecRandomRef; count: LongWord; bytes: PByte)
-  : Integer; cdecl; external libSecurity Name _PU + 'SecRandomCopyBytes';
-{$ENDIF IOSDELPHI}
-{$IFDEF IOSFPC}
+{$ENDIF}
+// ************************************************************************//
+{$IFDEF CRYPTOLIB_APPLE}
+{$IFDEF FPC}
 
 
 type
 type
   // similar to a TOpaqueData already defined in newer FPC but not available in 3.0.4
   // similar to a TOpaqueData already defined in newer FPC but not available in 3.0.4
@@ -162,37 +259,29 @@ type
   // similar to an OpaquePointer already defined in newer FPC but not available in 3.0.4
   // similar to an OpaquePointer already defined in newer FPC but not available in 3.0.4
   SecRandomRef = ^__SecRandom;
   SecRandomRef = ^__SecRandom;
 
 
-const
-  { * This is a synonym for NULL, if you'd rather use a named constant.   This
-    refers to a cryptographically secure random number generator.  * }
-  kSecRandomDefault: SecRandomRef = Nil;
-
 function SecRandomCopyBytes(rnd: SecRandomRef; count: LongWord; bytes: PByte)
 function SecRandomCopyBytes(rnd: SecRandomRef; count: LongWord; bytes: PByte)
-  : Integer; cdecl; external;
+  : Int32; cdecl; external;
 
 
-{$ENDIF IOSFPC}
+{$ELSE}
 
 
-implementation
+type
+  SecRandomRef = Pointer;
 
 
-class procedure TOSRandom.Boot;
-begin
-{$IFDEF MSWINDOWS}
-  FIsCngBCryptGenRandomSupportedOnOS := IsCngBCryptGenRandomAvailable();
-{$ENDIF MSWINDOWS}
-end;
+const
+  libSecurity = '/System/Library/Frameworks/Security.framework/Security';
 
 
-class constructor TOSRandom.OSRandom;
-begin
-  TOSRandom.Boot();
-end;
+function SecRandomCopyBytes(rnd: SecRandomRef; count: LongWord; bytes: PByte)
+  : Int32; cdecl; external libSecurity Name _PU + 'SecRandomCopyBytes';
 
 
-{$IFDEF IOSDELPHI}
+{$ENDIF}
+{$ENDIF}
+// ************************************************************************//
+{$IFDEF CRYPTOLIB_GENERIC_BSD}
+procedure arc4random_buf(bytes: PByte; count: LongWord); cdecl; external;
+// 'c' name 'arc4random_buf';
+{$ENDIF}
 
 
-function kSecRandomDefault: Pointer;
-begin
-  result := CocoaPointerConst(libSecurity, 'kSecRandomDefault');
-end;
-{$ENDIF IOSDELPHI}
+implementation
 
 
 class function TOSRandom.NoZeroes(const data: TCryptoLibByteArray): Boolean;
 class function TOSRandom.NoZeroes(const data: TCryptoLibByteArray): Boolean;
 var
 var
@@ -207,10 +296,24 @@ begin
       Exit;
       Exit;
     end;
     end;
   end;
   end;
+end;
 
 
+class procedure TOSRandom.Boot;
+begin
+{$IFDEF CRYPTOLIB_MSWINDOWS}
+  FIsCngBCryptGenRandomSupportedOnOS := IsCngBCryptGenRandomAvailable();
+{$ENDIF}
+{$IFDEF CRYPTOLIB_LINUX}
+  FIsGetRandomSupportedOnOS := IsGetRandomAvailable();
+{$ENDIF}
+end;
+
+class constructor TOSRandom.OSRandom;
+begin
+  TOSRandom.Boot();
 end;
 end;
 
 
-{$IF DEFINED(MSWINDOWS)}
+{$IFDEF CRYPTOLIB_MSWINDOWS}
 
 
 class function TOSRandom.GetIsCngBCryptGenRandomSupportedOnOS(): Boolean;
 class function TOSRandom.GetIsCngBCryptGenRandomSupportedOnOS(): Boolean;
 begin
 begin
@@ -247,8 +350,7 @@ begin
   end;
   end;
 end;
 end;
 
 
-class function TOSRandom.GenRandomBytesWindows(len: Int32;
-  const data: PByte): Int32;
+class function TOSRandom.GenRandomBytesWindows(len: Int32; data: PByte): Int32;
 
 
   function BCRYPT_SUCCESS(AStatus: NTStatus): Boolean; inline;
   function BCRYPT_SUCCESS(AStatus: NTStatus): Boolean; inline;
   begin
   begin
@@ -277,7 +379,7 @@ begin
 
 
     try
     try
       if (not BCRYPT_SUCCESS(FBCryptGenRandom(hProv, PUCHAR(data),
       if (not BCRYPT_SUCCESS(FBCryptGenRandom(hProv, PUCHAR(data),
-        LongWord(len), 0))) then
+        ULONG(len), 0))) then
       begin
       begin
         result := HResultFromWin32(GetLastError);
         result := HResultFromWin32(GetLastError);
         Exit;
         Exit;
@@ -297,7 +399,7 @@ begin
     end;
     end;
 
 
     try
     try
-      if not CryptGenRandom(hProv, len, data) then
+      if not CryptGenRandom(hProv, DWORD(len), data) then
       begin
       begin
         result := HResultFromWin32(GetLastError);
         result := HResultFromWin32(GetLastError);
         Exit;
         Exit;
@@ -309,26 +411,28 @@ begin
   result := S_OK;
   result := S_OK;
 end;
 end;
 
 
-{$ELSEIF DEFINED(IOSDELPHI)}
+{$ENDIF}
+{$IFDEF CRYPTOLIB_APPLE}
 
 
-class function TOSRandom.GenRandomBytesIOSDelphi(len: Int32;
-  const data: PByte): Int32;
-begin
-  result := SecRandomCopyBytes(kSecRandomDefault, LongWord(len), data);
-end;
+class function TOSRandom.GenRandomBytesApple(len: Int32; data: PByte): Int32;
 
 
-{$ELSEIF DEFINED(IOSFPC)}
+  function kSecRandomDefault: SecRandomRef;
+  begin
+{$IFDEF FPC}
+    result := Nil;
+{$ELSE}
+    result := CocoaPointerConst(libSecurity, 'kSecRandomDefault');
+{$ENDIF}
+  end;
 
 
-class function TOSRandom.GenRandomBytesIOSFPC(len: Int32;
-  const data: PByte): Int32;
 begin
 begin
-  // UNTESTED !!!, Please Take Note.
   result := SecRandomCopyBytes(kSecRandomDefault, LongWord(len), data);
   result := SecRandomCopyBytes(kSecRandomDefault, LongWord(len), data);
 end;
 end;
-{$ELSE}
 
 
-class function TOSRandom.GenRandomBytesUnix(len: Int32;
-  const data: PByte): Int32;
+{$ENDIF}
+{$IFDEF CRYPTOLIB_UNIX}
+
+class function TOSRandom.dev_random_device_read(len: Int32; data: PByte): Int32;
 var
 var
   LStream: TFileStream;
   LStream: TFileStream;
   RandGen: String;
   RandGen: String;
@@ -357,41 +461,125 @@ begin
     LStream.Free;
     LStream.Free;
   end;
   end;
 end;
 end;
+{$ENDIF}
+{$IFDEF CRYPTOLIB_LINUX}
 
 
-{$IFEND MSWINDOWS}
+class function TOSRandom.ErrorNo: Int32;
+begin
+  result := Errno;
+end;
+
+class function TOSRandom.GetIsGetRandomSupportedOnOS(): Boolean;
+begin
+  result := FIsGetRandomSupportedOnOS;
+end;
+
+class function TOSRandom.IsGetRandomAvailable(): Boolean;
+const
+  LIBC_SO_6 = 'libc.so.6';
+var
+  Lib: {$IFDEF FPC} PtrInt {$ELSE} NativeUInt {$ENDIF};
+begin
+  FGetRandom := Nil;
+  Lib := {$IFDEF FPC}PtrInt{$ENDIF}(dlopen(LIBC_SO_6, RTLD_NOW));
+  if Lib <> 0 then
+  begin
+    FGetRandom := dlsym(Lib, 'getrandom');
+    dlclose(Lib);
+  end;
+  result := System.Assigned(FGetRandom);
+end;
+
+class function TOSRandom.GenRandomBytesLinux(len: Int32; data: PByte): Int32;
+const
+  GRND_DEFAULT: Int32 = $0000;
+  EINTR = {$IFDEF FPC}ESysEINTR {$ELSE}Posix.Errno.EINTR{$ENDIF};
+var
+  n: Int64;
+begin
+  if IsGetRandomSupportedOnOS then
+  begin
+    while (len > 0) do
+    begin
+
+      repeat
+        n := FGetRandom(data, LongWord(len), GRND_DEFAULT);
+      until ((n > 0) and (ErrorNo <> EINTR));
+
+      if (n <= 0) then
+      begin
+        result := -1;
+        Exit;
+      end;
+      System.Inc(data, n);
+      System.Dec(len, n);
+    end;
+    result := 0;
+  end
+  else
+  begin
+    // fallback for when getrandom API is not available
+    result := dev_random_device_read(len, data);
+  end;
+end;
+
+{$ENDIF}
+{$IFDEF CRYPTOLIB_GENERIC_BSD}
+
+class function TOSRandom.GenRandomBytesGenericBSD(len: Int32;
+  data: PByte): Int32;
+
+begin
+  arc4random_buf(data, LongWord(len));
+  result := 0;
+end;
+{$ENDIF}
 
 
 class procedure TOSRandom.GetBytes(const data: TCryptoLibByteArray);
 class procedure TOSRandom.GetBytes(const data: TCryptoLibByteArray);
 var
 var
   count: Int32;
   count: Int32;
 begin
 begin
   count := System.Length(data);
   count := System.Length(data);
-{$IF DEFINED(MSWINDOWS)}
+
+  if count <= 0 then
+  begin
+    Exit;
+  end;
+
+{$IF DEFINED(CRYPTOLIB_MSWINDOWS)}
   if GenRandomBytesWindows(count, PByte(data)) <> 0 then
   if GenRandomBytesWindows(count, PByte(data)) <> 0 then
   begin
   begin
-    raise EAccessCryptoLibException.CreateRes
+    raise EOSRandomCryptoLibException.CreateRes
       (@SMSWIndowsCryptographyAPIGenerationError);
       (@SMSWIndowsCryptographyAPIGenerationError);
   end;
   end;
 
 
-{$ELSEIF DEFINED(IOSDELPHI)}
-  if GenRandomBytesIOSDelphi(count, PByte(data)) <> 0 then
+{$ELSEIF DEFINED(CRYPTOLIB_APPLE)}
+  if GenRandomBytesApple(count, PByte(data)) <> 0 then
   begin
   begin
-    raise EAccessCryptoLibException.CreateRes
-      (@SIOSSecRandomCopyBytesGenerationError);
+    raise EOSRandomCryptoLibException.CreateRes
+      (@SAppleSecRandomCopyBytesGenerationError);
   end;
   end;
 
 
-{$ELSEIF DEFINED(IOSFPC)}
-  if GenRandomBytesIOSFPC(count, PByte(data)) <> 0 then
+{$ELSEIF DEFINED(CRYPTOLIB_LINUX)}
+  if GenRandomBytesLinux(count, PByte(data)) <> 0 then
   begin
   begin
-    raise EAccessCryptoLibException.CreateRes
-      (@SIOSSecRandomCopyBytesGenerationError);
+    raise EOSRandomCryptoLibException.CreateRes(@SLinuxGetRandomError);
   end;
   end;
 
 
-{$ELSE}
-  if GenRandomBytesUnix(count, PByte(data)) <> 0 then
+{$ELSEIF DEFINED(CRYPTOLIB_GENERIC_BSD)}
+  if GenRandomBytesGenericBSD(count, PByte(data)) <> 0 then
   begin
   begin
-    raise EAccessCryptoLibException.CreateRes(@SUnixRandomReadError);
+    raise EOSRandomCryptoLibException.CreateRes(@SArc4RandomBufGenerationError);
   end;
   end;
-{$IFEND MSWINDOWS}
+{$ELSEIF DEFINED(CRYPTOLIB_UNDEFINED_UNIX_VARIANTS)}
+  // fallback option for other Undefined Unix OSes
+  if dev_random_device_read(count, PByte(data)) <> 0 then
+  begin
+    raise EOSRandomCryptoLibException.CreateRes(@SRandomDeviceReadError);
+  end;
+{$ELSE}
+{$MESSAGE ERROR 'UNSUPPORTED TARGET.'}
+{$IFEND}
 end;
 end;
 
 
 class procedure TOSRandom.GetNonZeroBytes(const data: TCryptoLibByteArray);
 class procedure TOSRandom.GetNonZeroBytes(const data: TCryptoLibByteArray);

+ 170 - 107
README.md

@@ -1,134 +1,197 @@
-# CryptoLib4Pascal [![License](http://img.shields.io/badge/license-MIT-green.svg)](https://github.com/Xor-el/CryptoLib4Pascal/blob/master/LICENSE)
-CryptoLib4Pascal is a Cryptographic Package for Delphi/FreePascal Compilers that provides at the moment support for creating, signing and verifying DSA, ECDSA, ECNR and ECSchnorr signatures using various curves and hashes, AES, Rijndael, Blowfish and Speck Block Encryption and Decryption (With various modes and paddings), Salsa20, XSalsa20, ChaCha Stream Ciphers and ECIES.
-
-**Supported Elliptic Curves:**
-
-Curves are grouped in three different classes.
-1. NistNamed Curves
-2. SecNamed Curves (these are bare generic implementation of curves (at the moment, not as optimized as their CustomNamed Curves Counterparts))
-3. CustomNamed Curves. (these are specially optimized implementations of curves found in the previous two.)
-
-**NistNamed Curves:**
-```
-K-283, B-283, B-409, B-233, K-163, P-521, K-233, K-409, P-192, P-384, P-224, P-256, B-163, B-571, K-571.
-```
-
-**SecNamed Curves:**
-```
-sect131r1, secp160k1, sect193r1, secp384r1, secp128r1, sect163k1, sect193r2, sect239k1, sect409r1, secp256r1,
-secp521r1, sect131r2, secp112r1, sect113r2, sect163r1, secp224k1, secp224r1, sect283k1, sect233k1, sect283r1,
-sect409k1, sect571k1, secp192r1, sect233r1, secp256k1, sect571r1, secp192k1, secp160r1, sect113r1, secp160r2,
-secp112r2, sect163r2, secp128r2.
-```
-
-**CustomNamed Curves:**
-
-| curve name | alias(es) |
-|--------|--------|
-|  **secp256k1**      |        |
-|  **secp256r1**      |   **P-256**    |
-|  **secp384r1**      |   **P-384**    |
-|  **secp521r1**      |   **P-521**    |
-|  **sect283k1**      |   **K-283**    |
-
-
-**Supported Algorithms:**
-    
-    Supported signing algorithms 
-    
-    ECDSA
-    NONEwithECDSA, SHA-1withECDSA, SHA-224withECDSA, 
-    SHA-256withECDSA, SHA-384withECDSA, SHA-512withECDSA and RIPEMD160withECDSA
-    
-    ECSchnorr
-    SHA-1withECSCHNORRSIPA, SHA-224withECSCHNORRSIPA, SHA-256withECSCHNORRSIPA, SHA-384withECSCHNORRSIPA,
-    SHA-512withECSCHNORRSIPA, RIPEMD160withECSCHNORRSIPA
-    
-    
-    
-   **Dependencies:**
-   
-   [HashLib4Pascal](https://github.com/Xor-el/HashLib4Pascal) >= v2.5
-   
-   [SimpleBaseLib4Pascal](https://github.com/Xor-el/SimpleBaseLib4Pascal) >= v1.4
-    
-   For FreePascal v3.0.x [Generics.Collections](https://github.com/maciej-izak/generics.collections)
-
-**Supported Compilers**
+CryptoLib4Pascal: Crypto for Modern Object Pascal [![License](http://img.shields.io/badge/license-MIT-green.svg)](https://github.com/Xor-el/CryptoLib4Pascal/blob/master/LICENSE)
+========================================
+
+``CryptoLib4Pascal`` is an Object Pascal cryptographic library released under the permissive [MIT License](https://github.com/Xor-el/CryptoLib4Pascal/blob/master/LICENSE). 
+
+``CryptoLib4Pascal's`` goal is to be the best option for cryptography in Object Pascal by offering cryptographic recipes and primitives to Object Pascal developers.
+
+Development is coordinated on [GitHub](https://github.com/Xor-el/CryptoLib4Pascal) and contributions are welcome. If you need help, please open an issue [here](https://github.com/Xor-el/CryptoLib4Pascal/issues).
+
+
+**Build Status**
+[![Build Status](https://travis-ci.org/Xor-el/CryptoLib4Pascal.svg?branch=master)](https://travis-ci.org/Xor-el/CryptoLib4Pascal)
+
+Available Algorithms
+----------------------------------------
+
+ ### Symmetric Encryption
+----------------------------------------
+###### Block Ciphers
+* `AES (128, 192, and 256)` 
+
+* `Rijndael` 
+
+* `Blowfish`
+
+* `Speck`
+
+###### Stream Ciphers
+* `ChaCha`
+
+* `(X)Salsa20` 
+
+##### Cipher Modes (for applicable algorithms) 
+----------------------------------------
+
+* `ECB` 
+
+* `CBC` 
+
+* `CFB` 
+
+* `CTR` 
+
+* `CTS` 
+
+* `OFB` 
+
+* `SIC`
+
+##### Padding Modes (for applicable algorithms) 
+----------------------------------------
+
+* `ISO 10126-2` 
+
+* `ISO 7816-4` 
+
+* `Bit (ISO/IEC 9797-1)` 
+
+* `PKCS#5` 
+
+* `PKCS#7`
  
  
-    FreePascal 3.0.4 and Above.
-    
-    Delphi Tokyo and Above. (might work in earlier versions though.)
+* `TBC (Trailing Bit Complement)` 
 
 
-**Installing the Library.**
+* `ANSI X9.23` 
 
 
-**Method One:**
+* `Zero`
 
 
- Use the Provided Packages in the "Packages" Folder.
+### Asymmetric Cryptography
+----------------------------------------
 
 
-**Method Two:**
+* `DSA`
 
 
- Add the Library Path and Sub Path to your Project Search Path.
+* `(DET)ECDSA (supported curves: NIST, X9.62, SEC2, Brainpool)`
 
 
-**Usage Examples.**
+* `ECNR`
+
+* `ECSchnorr`
+ 
+* `EdDSA (Ed25519, Ed25519Blake2B)`
 
 
-    Check the "CryptoLib.Samples" folder and the Unit Tests.
+### Key Agreement/Exchange
+----------------------------------------
 
 
- **Unit Tests.**
+* `ECDH`
 
 
-To Run Unit Tests,
+* `ECDHC`
+ 
+* `X25519` 
 
 
-**For FPC 3.0.4 and above**
+### Key Derivation Functions
+----------------------------------------
 
 
+* `HKDF` 
+ 
+* `KDF1`
 
 
-    Simply compile and run "CryptoLib.Tests" project in "FreePascal.Tests" Folder.
+* `KDF2`
 
 
-**For Delphi**
+###### Password Hashing Schemes (Password Based Key Derivation Functions)
+----------------------------------------
 
 
-   **Method One (Using DUnit Test Runner)**
+* `PBKDF2`
+ 
+* `Argon2 (2i, 2d and 2id variants)`
 
 
-     To Build and Run the Unit Tests For Delphi 10 Tokyo (should be similar for 
-     other versions)
-    
-    1). Open Project Options of Unit Test (CryptoLib.Tests) in "Delphi.Tests" Folder.
-    
-    2). Change Target to All Configurations (Or "Base" In Older Delphi Versions.)
-    
-    3). In Output directory add ".\$(Platform)\$(Config)" without the quotes.
-    
-    4). In Search path add "$(BDS)\Source\DUnit\src" without the quotes.
-    
-    5). In Unit output directory add "." without the quotes.
-    
-    6). In Unit scope names (If Available), Delete "DUnitX" from the List.
-    
-    Press Ok and save, then build and run.
-    
- **Method Two (Using TestInsight) (Preferred).**
+* `Scrypt`
 
 
-    1). Download and Install TestInsight.
-    
-    2). Open Project Options of Unit Test (CryptoLib.Tests.TestInsight) in "Delphi.Tests" 
-        Folder. 
+### MAC
+----------------------------------------
 
 
-    3). Change Target to All Configurations (Or "Base" In Older Delphi Versions.)
+* `HMAC (all supported hashes)`
+ 
+* `X25519` 
 
 
-    4). In Unit scope names (If Available), Delete "DUnitX" from the List.
+### Hashes
+----------------------------------------
 
 
-    5). To Use TestInsight, right-click on the project, then select 
-		"Enable for TestInsight" or "TestInsight Project".
-        Save Project then Build and Run Test Project through TestInsight. 
-        
-  **Acknowledgements**
+ * `MD2`
+
+ * `MD4`
+
+ * `MD5`
+
+ * `SHA-1`
+
+ * `SHA-2 (224, 256, 384, 512, 512-224, 512-256)`
+
+ * `Gost3411`
+
+ * `Gost3411-2012 (256, 512)`
+
+ * `RIPEMD (128, 256, 256, 320)`
+
+ * `Tiger`
+
+ * `WhirlPool`
+
+ * `Blake2B (160, 256, 384, 512)`
+ 
+ * `Blake2S (128, 160, 224, 256)`
+
+ * `SHA-3 (224, 256, 384, 512)`
  
  
-Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring the development of this library.
+ * `Keccak (224, 256, 288, 384, 512)`
+
+### XOF (Extendable Output Function)
+----------------------------------------
+
+* `Shake (Shake-128, Shake-256)`
+
+### Other Useful Things
+----------------------------------------
+
+* `RNG wrappers for system RNG`
 
 
-**License**
+* `ASN1 Parsing Utilities`
 
 
-This "Software" is Licensed Under  **`MIT License (MIT)`** .
+* `Base Encoding and Decoding Utilities`
+
+### Compile-Time Dependencies
+----------------------------------------
+
+* [HashLib4Pascal](https://github.com/Xor-el/HashLib4Pascal)
+* [SimpleBaseLib4Pascal](https://github.com/Xor-el/SimpleBaseLib4Pascal)
+
+### Supported Compilers
+----------------------------------------
+
+* `FreePascal 3.2.0+`
+
+* `Delphi Tokyo+`
+
+### Supported OSes
+----------------------------------------
+
+* `Windows XP+`
+
+* `Linux (Including Android and Raspberry PI)`
+
+* `macOS 10.7+`
+
+* `iOS 2.0+`
+
+* `FreeBSD, NetBSD, OpenBSD and DragonBSD`
+
+### Acknowledgements
+----------------------------------------
+
+* Thanks to [Sphere 10 Software](http://www.sphere10.com/) for sponsoring the development of this library.
+
+### Tip Jar
+----------------------------------------
 
 
-#### Tip Jar
 * :dollar: **Bitcoin**: `1MhFfW7tDuEHQSgie65uJcAfJgCNchGeKf`
 * :dollar: **Bitcoin**: `1MhFfW7tDuEHQSgie65uJcAfJgCNchGeKf`
 * :euro: **Ethereum**: `0x6c1DC21aeC49A822A4f1E3bf07c623C2C1978a98`
 * :euro: **Ethereum**: `0x6c1DC21aeC49A822A4f1E3bf07c623C2C1978a98`
 * :pound: **Pascalcoin**: `345367-40`
 * :pound: **Pascalcoin**: `345367-40`
-