Browse Source

Allow usage of CryptoLib4Pascal - Basic files

Add libraries "cryptolib4pascal" and "simplebaselib4pascal"
Added new units "UPCCryptoLib4Pascal", "UPCEncryption" and "UPCOpenSSLSignature"
Pascal Coin 6 years ago
parent
commit
5b3f8bd622
100 changed files with 43905 additions and 0 deletions
  1. 553 0
      src/core/UPCCryptoLib4Pascal.pas
  2. 100 0
      src/core/UPCEncryption.pas
  3. 127 0
      src/core/UPCOpenSSLSignature.pas
  4. 101 0
      src/libraries/cryptolib4pascal/ClpAbstractECMultiplier.pas
  5. 792 0
      src/libraries/cryptolib4pascal/ClpAesEngine.pas
  6. 709 0
      src/libraries/cryptolib4pascal/ClpAesLightEngine.pas
  7. 423 0
      src/libraries/cryptolib4pascal/ClpArrayUtils.pas
  8. 9987 0
      src/libraries/cryptolib4pascal/ClpAsn1Objects.pas
  9. 103 0
      src/libraries/cryptolib4pascal/ClpAsymmetricCipherKeyPair.pas
  10. 87 0
      src/libraries/cryptolib4pascal/ClpAsymmetricKeyParameter.pas
  11. 218 0
      src/libraries/cryptolib4pascal/ClpBaseKdfBytesGenerator.pas
  12. 5062 0
      src/libraries/cryptolib4pascal/ClpBigInteger.pas
  13. 225 0
      src/libraries/cryptolib4pascal/ClpBigIntegers.pas
  14. 525 0
      src/libraries/cryptolib4pascal/ClpBitConverter.pas
  15. 373 0
      src/libraries/cryptolib4pascal/ClpBits.pas
  16. 1200 0
      src/libraries/cryptolib4pascal/ClpBlockCipherModes.pas
  17. 555 0
      src/libraries/cryptolib4pascal/ClpBlowfishEngine.pas
  18. 172 0
      src/libraries/cryptolib4pascal/ClpBsiObjectIdentifiers.pas
  19. 549 0
      src/libraries/cryptolib4pascal/ClpBufferedBlockCipher.pas
  20. 372 0
      src/libraries/cryptolib4pascal/ClpBufferedCipherBase.pas
  21. 69 0
      src/libraries/cryptolib4pascal/ClpCheck.pas
  22. 154 0
      src/libraries/cryptolib4pascal/ClpCipherKeyGenerator.pas
  23. 458 0
      src/libraries/cryptolib4pascal/ClpCipherUtilities.pas
  24. 546 0
      src/libraries/cryptolib4pascal/ClpConverters.pas
  25. 168 0
      src/libraries/cryptolib4pascal/ClpCryptoApiRandomGenerator.pas
  26. 221 0
      src/libraries/cryptolib4pascal/ClpCryptoLibTypes.pas
  27. 361 0
      src/libraries/cryptolib4pascal/ClpCryptoProObjectIdentifiers.pas
  28. 492 0
      src/libraries/cryptolib4pascal/ClpCustomNamedCurves.pas
  29. 179 0
      src/libraries/cryptolib4pascal/ClpDigest.pas
  30. 214 0
      src/libraries/cryptolib4pascal/ClpDigestRandomGenerator.pas
  31. 534 0
      src/libraries/cryptolib4pascal/ClpDigestUtilities.pas
  32. 240 0
      src/libraries/cryptolib4pascal/ClpDsaDigestSigner.pas
  33. 66 0
      src/libraries/cryptolib4pascal/ClpDsaKeyGenerationParameters.pas
  34. 142 0
      src/libraries/cryptolib4pascal/ClpDsaKeyPairGenerator.pas
  35. 93 0
      src/libraries/cryptolib4pascal/ClpDsaKeyParameters.pas
  36. 136 0
      src/libraries/cryptolib4pascal/ClpDsaParameter.pas
  37. 146 0
      src/libraries/cryptolib4pascal/ClpDsaParameterGenerationParameters.pas
  38. 170 0
      src/libraries/cryptolib4pascal/ClpDsaParameters.pas
  39. 619 0
      src/libraries/cryptolib4pascal/ClpDsaParametersGenerator.pas
  40. 98 0
      src/libraries/cryptolib4pascal/ClpDsaPrivateKeyParameters.pas
  41. 118 0
      src/libraries/cryptolib4pascal/ClpDsaPublicKeyParameters.pas
  42. 274 0
      src/libraries/cryptolib4pascal/ClpDsaSigner.pas
  43. 121 0
      src/libraries/cryptolib4pascal/ClpDsaValidationParameters.pas
  44. 1800 0
      src/libraries/cryptolib4pascal/ClpECAlgorithms.pas
  45. 6505 0
      src/libraries/cryptolib4pascal/ClpECC.pas
  46. 43 0
      src/libraries/cryptolib4pascal/ClpECCurveConstants.pas
  47. 152 0
      src/libraries/cryptolib4pascal/ClpECDHBasicAgreement.pas
  48. 148 0
      src/libraries/cryptolib4pascal/ClpECDHCBasicAgreement.pas
  49. 237 0
      src/libraries/cryptolib4pascal/ClpECDomainParameters.pas
  50. 402 0
      src/libraries/cryptolib4pascal/ClpECDsaSigner.pas
  51. 409 0
      src/libraries/cryptolib4pascal/ClpECGost3410NamedCurves.pas
  52. 104 0
      src/libraries/cryptolib4pascal/ClpECIESPublicKeyParser.pas
  53. 85 0
      src/libraries/cryptolib4pascal/ClpECKeyGenerationParameters.pas
  54. 241 0
      src/libraries/cryptolib4pascal/ClpECKeyPairGenerator.pas
  55. 234 0
      src/libraries/cryptolib4pascal/ClpECKeyParameters.pas
  56. 295 0
      src/libraries/cryptolib4pascal/ClpECNRSigner.pas
  57. 190 0
      src/libraries/cryptolib4pascal/ClpECNamedCurveTable.pas
  58. 121 0
      src/libraries/cryptolib4pascal/ClpECPrivateKeyParameters.pas
  59. 129 0
      src/libraries/cryptolib4pascal/ClpECPublicKeyParameters.pas
  60. 292 0
      src/libraries/cryptolib4pascal/ClpECSchnorrSipaSigner.pas
  61. 147 0
      src/libraries/cryptolib4pascal/ClpEacObjectIdentifiers.pas
  62. 103 0
      src/libraries/cryptolib4pascal/ClpEncoders.pas
  63. 71 0
      src/libraries/cryptolib4pascal/ClpEphemeralKeyPair.pas
  64. 68 0
      src/libraries/cryptolib4pascal/ClpEphemeralKeyPairGenerator.pas
  65. 132 0
      src/libraries/cryptolib4pascal/ClpFiniteFields.pas
  66. 130 0
      src/libraries/cryptolib4pascal/ClpFixedPointCombMultiplier.pas
  67. 114 0
      src/libraries/cryptolib4pascal/ClpFixedPointPreCompInfo.pas
  68. 219 0
      src/libraries/cryptolib4pascal/ClpFixedPointUtilities.pas
  69. 109 0
      src/libraries/cryptolib4pascal/ClpGF2Polynomial.pas
  70. 387 0
      src/libraries/cryptolib4pascal/ClpGeneratorUtilities.pas
  71. 133 0
      src/libraries/cryptolib4pascal/ClpGenericPolynomialExtensionField.pas
  72. 106 0
      src/libraries/cryptolib4pascal/ClpGlvMultiplier.pas
  73. 132 0
      src/libraries/cryptolib4pascal/ClpGlvTypeBEndomorphism.pas
  74. 120 0
      src/libraries/cryptolib4pascal/ClpGlvTypeBParameters.pas
  75. 150 0
      src/libraries/cryptolib4pascal/ClpHMac.pas
  76. 228 0
      src/libraries/cryptolib4pascal/ClpHMacDsaKCalculator.pas
  77. 231 0
      src/libraries/cryptolib4pascal/ClpHkdfBytesGenerator.pas
  78. 203 0
      src/libraries/cryptolib4pascal/ClpHkdfParameters.pas
  79. 40 0
      src/libraries/cryptolib4pascal/ClpIAbstractECMultiplier.pas
  80. 36 0
      src/libraries/cryptolib4pascal/ClpIAesEngine.pas
  81. 36 0
      src/libraries/cryptolib4pascal/ClpIAesLightEngine.pas
  82. 765 0
      src/libraries/cryptolib4pascal/ClpIAsn1Objects.pas
  83. 48 0
      src/libraries/cryptolib4pascal/ClpIAsymmetricCipherKeyPair.pas
  84. 50 0
      src/libraries/cryptolib4pascal/ClpIAsymmetricCipherKeyPairGenerator.pas
  85. 43 0
      src/libraries/cryptolib4pascal/ClpIAsymmetricKeyParameter.pas
  86. 36 0
      src/libraries/cryptolib4pascal/ClpIBaseKdfBytesGenerator.pas
  87. 58 0
      src/libraries/cryptolib4pascal/ClpIBasicAgreement.pas
  88. 68 0
      src/libraries/cryptolib4pascal/ClpIBlockCipher.pas
  89. 89 0
      src/libraries/cryptolib4pascal/ClpIBlockCipherModes.pas
  90. 84 0
      src/libraries/cryptolib4pascal/ClpIBlockCipherPadding.pas
  91. 36 0
      src/libraries/cryptolib4pascal/ClpIBlowfishEngine.pas
  92. 36 0
      src/libraries/cryptolib4pascal/ClpIBufferedBlockCipher.pas
  93. 107 0
      src/libraries/cryptolib4pascal/ClpIBufferedCipher.pas
  94. 35 0
      src/libraries/cryptolib4pascal/ClpIBufferedCipherBase.pas
  95. 57 0
      src/libraries/cryptolib4pascal/ClpICipherKeyGenerator.pas
  96. 36 0
      src/libraries/cryptolib4pascal/ClpICipherParameters.pas
  97. 35 0
      src/libraries/cryptolib4pascal/ClpICryptoApiRandomGenerator.pas
  98. 55 0
      src/libraries/cryptolib4pascal/ClpIDerivationFunction.pas
  99. 36 0
      src/libraries/cryptolib4pascal/ClpIDerivationParameters.pas
  100. 97 0
      src/libraries/cryptolib4pascal/ClpIDigest.pas

+ 553 - 0
src/core/UPCCryptoLib4Pascal.pas

@@ -0,0 +1,553 @@
+unit UPCCryptoLib4Pascal;
+
+{ Copyright (c) 2019 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  This unit contains code made by Ugochukwu Mmaduekwe (aka Xor-el at GitHub)
+  https://github.com/PascalCoin/PascalCoinTools/tree/master/Tools/PascalCoinKeyTool
+  Available under MIT License
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{
+  This unit is a bridge between PascalCoin and CryptoLib4Pascal in order to
+  do not use OpenSSL library to do some cryptographic functions.
+
+  Specially will define:
+  - Use of BigIntegers
+  - ECDSA keys generation
+  - ECDSA Sign
+  - ECDSA Verify
+  - PascalCoin ECDSA encryption
+  - PascalCoin AES encryption
+}
+
+interface
+
+Uses SysUtils, UBaseTypes, UPCDataTypes,
+  ClpBigInteger,
+  ClpSecureRandom,
+  ClpISecureRandom,
+  ClpIECDomainParameters,
+  ClpECDomainParameters,
+  ClpIX9ECParameters,
+  ClpIIESEngine,
+  ClpIBaseKdfBytesGenerator,
+  ClpIIESWithCipherParameters,
+  ClpIPascalCoinECIESKdfBytesGenerator,
+  ClpIPascalCoinIESEngine
+  ;
+
+Type
+  TPCCryptoLib4Pascal = Class
+  strict private
+  const
+    PKCS5_SALT_LEN = Int32(8);
+    SALT_MAGIC_LEN = Int32(8);
+    SALT_SIZE = Int32(8);
+    SALT_MAGIC: string = 'Salted__';
+  strict private
+    class var FRandom: ISecureRandom;
+    class var FDomain_SECP256K1 : IECDomainParameters;
+    class var FDomain_SECP384R1 : IECDomainParameters;
+    class var FDomain_SECT283K1 : IECDomainParameters;
+    class var FDomain_SECP521R1 : IECDomainParameters;
+    class var FCurve_SECP256K1 : IX9ECParameters;
+    class var FCurve_SECP384R1 : IX9ECParameters;
+    class var FCurve_SECT283K1 : IX9ECParameters;
+    class var FCurve_SECP521R1 : IX9ECParameters;
+    class var FPascalCoinIESEngine : IPascalCoinIESEngine;
+    class var FPascalCoinIESWithCipherParameters : IIESWithCipherParameters;
+    class constructor TPCCryptoLib4Pascal();
+    class function GetCurveAndDomainParameters(const AEC_OpenSSL_NID : Word; var OCurve : IX9ECParameters; var ODomain : IECDomainParameters; ARaiseIfNotForPascal : Boolean = True ) : Boolean;
+    class function GetDomainParameters(const AEC_OpenSSL_NID : Word) : IECDomainParameters;
+    class function EVP_GetSalt(): TBytes; static; inline;
+    class function EVP_GetKeyIV(const APasswordBytes, ASaltBytes: TBytes; out AKeyBytes, AIVBytes: TBytes): boolean; static;
+  protected
+  public
+    class function DoECDSASign(const AEC_OpenSSL_NID : Word; const APrivateKey, AMessage: TBytes; var ASignature : TECDSA_SIG) : Boolean;
+    class function DoECDSAVerify(const APublicKey : TECDSA_Public; const AMessage: TBytes; const ASignature : TECDSA_SIG) : Boolean;
+    class function DoPascalCoinECIESEncrypt(const APublicKey : TECDSA_Public; const AMessage : TBytes; var AEncryptedMessage : TBytes) : Boolean;
+    class function DoPascalCoinECIESDecrypt(const AEC_OpenSSL_NID : Word; const APrivateKey, AEncryptedMessage : TBytes; var ADecryptedMessage : TBytes) : Boolean;
+    class function DoPascalCoinAESEncrypt(const AMessage, APassword: TBytes): TBytes; static;
+    class function DoPascalCoinAESDecrypt(const AEncryptedMessage, APassword: TBytes; out ADecryptedMessage: TBytes): boolean; static;
+    class function DoGetRandomPrivateKey(const AEC_OpenSSL_NID : Word) : TBytes;
+    class function DoGetPublicKey(const AEC_OpenSSL_NID : Word; const APrivateKey: TBytes) : TECDSA_Public;
+    class procedure DoSHA256(const AInput : TBytes; var AOutput : TBytes);
+    class procedure DoRIPEMD160(const AInput : TBytes; var AOutput : TBytes);
+  End;
+
+implementation
+
+Uses
+  ClpCustomNamedCurves,
+  ClpIECPrivateKeyParameters,
+  ClpECPrivateKeyParameters,
+  ClpIParametersWithRandom,
+  ClpParametersWithRandom,
+  ClpIECDsaSigner,
+  ClpECDsaSigner,
+  ClpCryptoLibTypes,
+  //
+  ClpIECPublicKeyParameters,
+  ClpECPublicKeyParameters,
+  ClpIECC,
+  //
+  ClpIIESCipher,
+  ClpIESCipher,
+  ClpIBufferedBlockCipher,
+  ClpIAesEngine,
+  ClpIBlockCipherModes,
+  ClpIBasicAgreement,
+  ClpIESWithCipherParameters,
+  ClpIECDHBasicAgreement,
+  ClpIMac,
+  ClpECDHBasicAgreement,
+  ClpPascalCoinECIESKdfBytesGenerator,
+  ClpDigestUtilities,
+  ClpMacUtilities,
+  ClpAesEngine,
+  ClpBlockCipherModes,
+  ClpPaddedBufferedBlockCipher,
+  ClpPaddingModes,
+  ClpIPaddingModes,
+  ClpPascalCoinIESEngine,
+  //
+  ClpIParametersWithIV,
+  ClpIBufferedCipher,
+  ClpArrayUtils,
+  ClpCipherUtilities,
+  ClpParametersWithIV,
+  ClpParameterUtilities,
+  ClpIDigest,
+  //
+  ClpIAsymmetricCipherKeyPairGenerator,
+  ClpGeneratorUtilities,
+  ClpECKeyGenerationParameters,
+  ClpIECKeyGenerationParameters,
+  ClpIAsymmetricCipherKeyPair,
+  ClpECKeyPairGenerator,
+  //
+  HlpSHA2_256,
+  //
+  UAccounts,
+  UConst,
+  ULog;
+
+{ TPCCryptoLib4Pascal }
+
+class function TPCCryptoLib4Pascal.DoECDSASign(const AEC_OpenSSL_NID : Word;
+  Const APrivateKey, AMessage: TBytes; var ASignature: TECDSA_SIG): Boolean;
+var
+  LDomain: IECDomainParameters;
+  LPrivD: TBigInteger;
+  LPrivKeyParams : IECPrivateKeyParameters;
+  LParam: IParametersWithRandom;
+  LSigner : IECDsaSigner;
+  LSignerResult : TCryptoLibGenericArray<TBigInteger>;
+begin
+  LDomain := GetDomainParameters(AEC_OpenSSL_NID);
+  LPrivD := TBigInteger.Create(1, APrivateKey);
+  LPrivKeyParams := TECPrivateKeyParameters.Create('ECDSA',LPrivD,LDomain);
+  //
+  LParam := TParametersWithRandom.Create(LPrivKeyParams, FRandom);
+  LSigner := TECDsaSigner.Create();
+  LSigner.Init(True, LParam);
+  LSignerResult := LSigner.GenerateSignature(AMessage);
+  ASignature.r := LSignerResult[0].ToByteArray();
+  ASignature.s := LSignerResult[1].ToByteArray();
+  Result := True;
+end;
+
+class function TPCCryptoLib4Pascal.DoECDSAVerify(const APublicKey: TECDSA_Public; const AMessage: TBytes; const ASignature: TECDSA_SIG): Boolean;
+var
+  LDomain: IECDomainParameters;
+  LCurve: IX9ECParameters;
+  LSigner: IECDsaSigner;
+  LPubKeyParams : IECPublicKeyParameters;
+  LPoint: IECPoint;
+  LBigXCoord, LBigYCoord,
+  LSigR, LSigS: TBigInteger;
+begin
+  GetCurveAndDomainParameters(APublicKey.EC_OpenSSL_NID,LCurve,LDomain);
+  LBigXCoord := TBigInteger.Create(1, APublicKey.x);
+  LBigYCoord := TBigInteger.Create(1, APublicKey.y);
+  LPoint := LCurve.Curve.CreatePoint(LBigXCoord, LBigYCoord);
+  LPubKeyParams := TECPublicKeyParameters.Create('ECDSA', LPoint, LDomain);
+
+  LSigR := TBigInteger.Create(1, ASignature.r);
+  LSigS := TBigInteger.Create(1, ASignature.s);
+
+  LSigner := TECDsaSigner.Create();
+  LSigner.Init(False, LPubKeyParams);
+  Result := LSigner.VerifySignature(AMessage, LSigR, LSigS);
+end;
+
+class function TPCCryptoLib4Pascal.DoGetPublicKey(const AEC_OpenSSL_NID: Word; const APrivateKey: TBytes): TECDSA_Public;
+var
+  LDomain: IECDomainParameters;
+  LPrivD : TBigInteger;
+  LECPrivateKeyParameters : IECPrivateKeyParameters;
+  LPublicKey: IECPublicKeyParameters;
+begin
+  LDomain := GetDomainParameters(AEC_OpenSSL_NID);
+
+  LPrivD := TBigInteger.Create(1,APrivateKey); // Obtain a big num based on private key
+  LECPrivateKeyParameters := TECPrivateKeyParameters.Create('ECDSA',LPrivD,LDomain);
+
+  LPublicKey := TECKeyPairGenerator.GetCorrespondingPublicKey(LECPrivateKeyParameters);
+
+  Result.EC_OpenSSL_NID := AEC_OpenSSL_NID;
+  Result.X := LPublicKey.Q.AffineXCoord.ToBigInteger().ToByteArray();
+  Result.Y := LPublicKey.Q.AffineYCoord.ToBigInteger().ToByteArray();
+end;
+
+class function TPCCryptoLib4Pascal.DoGetRandomPrivateKey(const AEC_OpenSSL_NID: Word): TBytes;
+var
+  LDomain: IECDomainParameters;
+  LCurve: IX9ECParameters;
+  KeyPairGeneratorInstance: IAsymmetricCipherKeyPairGenerator;
+  LKeyPair: IAsymmetricCipherKeyPair;
+  LPrivateKey: IECPrivateKeyParameters;
+begin
+  GetCurveAndDomainParameters(AEC_OpenSSL_NID,LCurve,LDomain);
+  KeyPairGeneratorInstance := TGeneratorUtilities.GetKeyPairGenerator('ECDSA');
+  KeyPairGeneratorInstance.Init(TECKeyGenerationParameters.Create(LDomain, FRandom) as IECKeyGenerationParameters);
+  LKeyPair := KeyPairGeneratorInstance.GenerateKeyPair();
+  LPrivateKey := LKeyPair.&Private as IECPrivateKeyParameters;
+  Result := LPrivateKey.D.ToByteArray();
+end;
+
+class function TPCCryptoLib4Pascal.DoPascalCoinAESDecrypt(
+  const AEncryptedMessage, APassword: TBytes; out ADecryptedMessage: TBytes): boolean;
+var
+  SaltBytes, KeyBytes, IVBytes, Buf, Chopped: TRawBytes;
+  KeyParametersWithIV: IParametersWithIV;
+  cipher: IBufferedCipher;
+  LBufStart, LSrcStart, LCount: Int32;
+begin
+  try
+    System.SetLength(SaltBytes, SALT_SIZE);
+    // First read the magic text and the salt - if any
+    Chopped := System.Copy(AEncryptedMessage, 0, SALT_MAGIC_LEN);
+    if (System.Length(AEncryptedMessage) >= SALT_MAGIC_LEN) and
+      (Chopped.ToString = SALT_MAGIC) then
+    begin
+      System.Move(AEncryptedMessage[SALT_MAGIC_LEN], SaltBytes[0], SALT_SIZE);
+      if not EVP_GetKeyIV(APassword, SaltBytes, KeyBytes, IVBytes) then
+      begin
+        Result := False;
+        Exit;
+      end;
+      LSrcStart := SALT_MAGIC_LEN + SALT_SIZE;
+    end
+    else
+    begin
+      if not EVP_GetKeyIV(APassword, nil, KeyBytes, IVBytes) then
+      begin
+        Result := False;
+        Exit;
+      end;
+      LSrcStart := 0;
+    end;
+
+    cipher := TCipherUtilities.GetCipher('AES/CBC/PKCS7PADDING');
+    KeyParametersWithIV := TParametersWithIV.Create(TParameterUtilities.CreateKeyParameter('AES', KeyBytes), IVBytes);
+
+    cipher.Init(False, KeyParametersWithIV); // init decryption cipher
+
+    System.SetLength(Buf, System.Length(AEncryptedMessage));
+
+    LBufStart := 0;
+
+    LCount := cipher.ProcessBytes(AEncryptedMessage, LSrcStart, System.Length(AEncryptedMessage) - LSrcStart, Buf, LBufStart);
+    System.Inc(LBufStart, LCount);
+    LCount := cipher.DoFinal(Buf, LBufStart);
+    System.Inc(LBufStart, LCount);
+
+    System.SetLength(Buf, LBufStart);
+
+    ADecryptedMessage := System.Copy(Buf);
+
+    Result := True;
+  except
+    Result := False;
+  end;
+end;
+
+class function TPCCryptoLib4Pascal.DoPascalCoinAESEncrypt(const AMessage, APassword: TBytes): TBytes;
+var
+  SaltBytes, KeyBytes, IVBytes, Buf, LAuxBuf: TRawBytes;
+  KeyParametersWithIV: IParametersWithIV;
+  cipher: IBufferedCipher;
+  LBlockSize, LBufStart, Count: Int32;
+begin
+  SaltBytes := EVP_GetSalt();
+  EVP_GetKeyIV(APassword, SaltBytes, KeyBytes, IVBytes);
+  cipher := TCipherUtilities.GetCipher('AES/CBC/PKCS7PADDING');
+  KeyParametersWithIV := TParametersWithIV.Create
+    (TParameterUtilities.CreateKeyParameter('AES', KeyBytes), IVBytes);
+
+  cipher.Init(True, KeyParametersWithIV); // init encryption cipher
+  LBlockSize := cipher.GetBlockSize;
+
+  System.SetLength(Buf, System.Length(AMessage) + LBlockSize + SALT_MAGIC_LEN + PKCS5_SALT_LEN);
+
+  LBufStart := 0;
+
+  LAuxBuf.FromString(SALT_MAGIC);
+  System.Move(LAuxBuf[0], Buf[LBufStart], SALT_MAGIC_LEN * System.SizeOf(byte));
+  System.Inc(LBufStart, SALT_MAGIC_LEN);
+  System.Move(SaltBytes[0], Buf[LBufStart], PKCS5_SALT_LEN * System.SizeOf(byte));
+  System.Inc(LBufStart, PKCS5_SALT_LEN);
+
+  Count := cipher.ProcessBytes(AMessage, 0, System.Length(AMessage), Buf, LBufStart);
+  System.Inc(LBufStart, Count);
+  Count := cipher.DoFinal(Buf, LBufStart);
+  System.Inc(LBufStart, Count);
+
+  System.SetLength(Buf, LBufStart);
+  Result := Buf;
+end;
+
+class function TPCCryptoLib4Pascal.DoPascalCoinECIESDecrypt(
+  const AEC_OpenSSL_NID: Word; const APrivateKey, AEncryptedMessage: TBytes;
+  var ADecryptedMessage: TBytes): Boolean;
+var
+  LDomain: IECDomainParameters;
+  LPrivD: TBigInteger;
+  LPrivKeyParams : IECPrivateKeyParameters;
+
+  LCurve: IX9ECParameters;
+  LBigXCoord, LBigYCoord : TBigInteger;
+  LPoint: IECPoint;
+  LPubKeyParams : IECPublicKeyParameters;
+  //
+  LCipherDecrypt: IIESCipher;
+begin
+  try
+    LDomain := GetDomainParameters(AEC_OpenSSL_NID);
+    LPrivD := TBigInteger.Create(1, APrivateKey);
+    LPrivKeyParams := TECPrivateKeyParameters.Create('ECDSA',LPrivD,LDomain);
+
+    // Decryption
+    LCipherDecrypt := TIESCipher.Create(FPascalCoinIESEngine);
+    LCipherDecrypt.Init(False, LPrivKeyParams, FPascalCoinIESWithCipherParameters, FRandom);
+    ADecryptedMessage := System.Copy(LCipherDecrypt.DoFinal(AEncryptedMessage));
+    Result := True;
+  except
+    Result := False;
+  end;
+end;
+
+class function TPCCryptoLib4Pascal.DoPascalCoinECIESEncrypt(
+  const APublicKey: TECDSA_Public; const AMessage: TBytes;
+  var AEncryptedMessage: TBytes): Boolean;
+var
+  LDomain: IECDomainParameters;
+  LCurve: IX9ECParameters;
+  LBigXCoord, LBigYCoord : TBigInteger;
+  LPoint: IECPoint;
+  LPubKeyParams : IECPublicKeyParameters;
+  //
+  LCipherEncrypt: IIESCipher;
+begin
+  GetCurveAndDomainParameters(APublicKey.EC_OpenSSL_NID,LCurve,LDomain);
+  LBigXCoord := TBigInteger.Create(1, APublicKey.x);
+  LBigYCoord := TBigInteger.Create(1, APublicKey.y);
+  LPoint := LCurve.Curve.CreatePoint(LBigXCoord, LBigYCoord);
+  LPubKeyParams := TECPublicKeyParameters.Create('ECDSA', LPoint, LDomain);
+  // Encryption
+  LCipherEncrypt := TIESCipher.Create(FPascalCoinIESEngine);
+  LCipherEncrypt.Init(True, LPubKeyParams, FPascalCoinIESWithCipherParameters, FRandom);
+  AEncryptedMessage := LCipherEncrypt.DoFinal(AMessage);
+  Result := True;
+end;
+
+class procedure TPCCryptoLib4Pascal.DoRIPEMD160(const AInput: TBytes; var AOutput: TBytes);
+begin
+  AOutput := TDigestUtilities.CalculateDigest('RIPEMD160', AInput);
+end;
+
+class procedure TPCCryptoLib4Pascal.DoSHA256(const AInput: TBytes; var AOutput: TBytes);
+begin
+  AOutput := TDigestUtilities.CalculateDigest('SHA-256', AInput);
+end;
+
+class function TPCCryptoLib4Pascal.EVP_GetKeyIV(const APasswordBytes,
+  ASaltBytes: TBytes; out AKeyBytes, AIVBytes: TBytes): boolean;
+var
+  LKey, LIV: Int32;
+  LDigest: IDigest;
+begin
+  LKey := 32; // AES256 CBC Key Length
+  LIV := 16; // AES256 CBC IV Length
+  System.SetLength(AKeyBytes, LKey);
+  System.SetLength(AIVBytes, LKey);
+  // Max size to start then reduce it at the end
+  LDigest := TDigestUtilities.GetDigest('SHA-256'); // SHA2_256
+  System.Assert(LDigest.GetDigestSize >= LKey);
+  System.Assert(LDigest.GetDigestSize >= LIV);
+  // Derive Key First
+  LDigest.BlockUpdate(APasswordBytes, 0, System.Length(APasswordBytes));
+  if ASaltBytes <> Nil then
+  begin
+    LDigest.BlockUpdate(ASaltBytes, 0, System.Length(ASaltBytes));
+  end;
+  LDigest.DoFinal(AKeyBytes, 0);
+  // Derive IV Next
+  LDigest.Reset();
+  LDigest.BlockUpdate(AKeyBytes, 0, System.Length(AKeyBytes));
+  LDigest.BlockUpdate(APasswordBytes, 0, System.Length(APasswordBytes));
+  if ASaltBytes <> Nil then
+  begin
+    LDigest.BlockUpdate(ASaltBytes, 0, System.Length(ASaltBytes));
+  end;
+  LDigest.DoFinal(AIVBytes, 0);
+
+  System.SetLength(AIVBytes, LIV);
+  Result := True;
+end;
+
+class function TPCCryptoLib4Pascal.EVP_GetSalt: TBytes;
+begin
+  System.SetLength(Result, PKCS5_SALT_LEN);
+  FRandom.NextBytes(Result);
+
+end;
+
+class function TPCCryptoLib4Pascal.GetCurveAndDomainParameters(
+  const AEC_OpenSSL_NID: Word; var OCurve: IX9ECParameters;
+  var ODomain: IECDomainParameters; ARaiseIfNotForPascal: Boolean): Boolean;
+begin
+  Result := True;
+  case AEC_OpenSSL_NID of
+    CT_NID_secp256k1 : begin
+      OCurve := FCurve_SECP256K1;
+      ODomain := FDomain_SECP256K1;
+    end;
+    CT_NID_secp384r1 : begin
+      OCurve := FCurve_SECP384R1;
+      ODomain := FDomain_SECP384R1;
+    end;
+    CT_NID_secp521r1 : begin
+      OCurve := FCurve_SECP521R1;
+      ODomain := FDomain_SECP521R1;
+    end;
+    CT_NID_sect283k1 : begin
+      OCurve := FCurve_SECT283K1;
+      ODomain := FDomain_SECT283K1;
+    end;
+  else
+    if ARaiseIfNotForPascal then raise Exception.Create(Format('Invalid Curve type:%d',[AEC_OpenSSL_NID]))
+    else Result := False;
+  end;
+end;
+
+class function TPCCryptoLib4Pascal.GetDomainParameters(const AEC_OpenSSL_NID: Word): IECDomainParameters;
+begin
+  case AEC_OpenSSL_NID of
+    CT_NID_secp256k1 : Result := FDomain_SECP256K1;
+    CT_NID_secp384r1 : Result := FDomain_SECP384R1;
+    CT_NID_secp521r1 : Result := FDomain_SECP521R1;
+    CT_NID_sect283k1 : Result := FDomain_SECT283K1;
+  else raise Exception.Create(Format('Invalid Curve type:%d',[AEC_OpenSSL_NID]));
+  end;
+end;
+
+class constructor TPCCryptoLib4Pascal.TPCCryptoLib4Pascal;
+    function GetIESCipherParameters: IIESWithCipherParameters;
+    var
+      Derivation, Encoding, IVBytes: TBytes;
+      MacKeySizeInBits, CipherKeySizeInBits: Int32;
+      UsePointCompression: boolean;
+    begin
+      // Set up IES Cipher Parameters For Compatibility With PascalCoin Current Implementation
+
+      // The derivation and encoding vectors are used when initialising the KDF and MAC.
+      // They're optional but if used then they need to be known by the other user so that
+      // they can decrypt the ciphertext and verify the MAC correctly. The security is based
+      // on the shared secret coming from the (static-ephemeral) ECDH key agreement.
+      Derivation := nil;
+
+      Encoding := nil;
+
+      System.SetLength(IVBytes, 16); // using Zero Initialized IV for compatibility
+
+      MacKeySizeInBits := 32 * 8;
+
+      // Since we are using AES256_CBC for compatibility
+      CipherKeySizeInBits := 32 * 8;
+
+      // whether to use point compression when deriving the octets string
+      // from a point or not in the EphemeralKeyPairGenerator
+      UsePointCompression := True; // for compatibility
+
+      Result := TIESWithCipherParameters.Create(Derivation, Encoding,
+        MacKeySizeInBits, CipherKeySizeInBits, IVBytes, UsePointCompression);
+    end;
+    function GetECIESPascalCoinCompatibilityEngine(): IPascalCoinIESEngine;
+    var
+      cipher: IBufferedBlockCipher;
+      AesEngine: IAesEngine;
+      blockCipher: ICbcBlockCipher;
+      ECDHBasicAgreementInstance: IECDHBasicAgreement;
+      KDFInstance: IPascalCoinECIESKdfBytesGenerator;
+      DigestMACInstance: IMac;
+
+    begin
+      // Set up IES Cipher Engine For Compatibility With PascalCoin
+
+      ECDHBasicAgreementInstance := TECDHBasicAgreement.Create();
+
+      KDFInstance := TPascalCoinECIESKdfBytesGenerator.Create
+        (TDigestUtilities.GetDigest('SHA-512'));
+
+      DigestMACInstance := TMacUtilities.GetMac('HMAC-MD5');
+
+      // Set Up Block Cipher
+      AesEngine := TAesEngine.Create(); // AES Engine
+
+      blockCipher := TCbcBlockCipher.Create(AesEngine); // CBC
+
+      cipher := TPaddedBufferedBlockCipher.Create(blockCipher,
+        TZeroBytePadding.Create() as IZeroBytePadding); // ZeroBytePadding
+
+      Result := TPascalCoinIESEngine.Create(ECDHBasicAgreementInstance, KDFInstance,
+        DigestMACInstance, cipher);
+    end;
+var  LCipher: IBufferedBlockCipher;
+  LAesEngine: IAesEngine;
+  blockCipher: ICbcBlockCipher;
+  ECDHBasicAgreementInstance: IECDHBasicAgreement;
+  KDFInstance: IPascalCoinECIESKdfBytesGenerator;
+  DigestMACInstance: IMac;
+begin
+  FRandom := TSecureRandom.Create();
+  // Init Curves and Domains for quick usage
+  FCurve_SECP256K1 := TCustomNamedCurves.GetByName('SECP256K1');
+  FDomain_SECP256K1 := TECDomainParameters.Create(FCurve_SECP256K1.Curve, FCurve_SECP256K1.G, FCurve_SECP256K1.N, FCurve_SECP256K1.H, FCurve_SECP256K1.GetSeed);
+  FCurve_SECP384R1 := TCustomNamedCurves.GetByName('SECP384R1');
+  FDomain_SECP384R1 := TECDomainParameters.Create(FCurve_SECP384R1.Curve, FCurve_SECP384R1.G, FCurve_SECP384R1.N, FCurve_SECP384R1.H, FCurve_SECP384R1.GetSeed);
+  FCurve_SECT283K1 := TCustomNamedCurves.GetByName('SECT283K1');
+  FDomain_SECT283K1 := TECDomainParameters.Create(FCurve_SECT283K1.Curve, FCurve_SECT283K1.G, FCurve_SECT283K1.N, FCurve_SECT283K1.H, FCurve_SECT283K1.GetSeed);
+  FCurve_SECP521R1 := TCustomNamedCurves.GetByName('SECP521R1');
+  FDomain_SECP521R1 := TECDomainParameters.Create(FCurve_SECP521R1.Curve, FCurve_SECP521R1.G, FCurve_SECP521R1.N, FCurve_SECP521R1.H, FCurve_SECP521R1.GetSeed);
+  // Init ECIES
+  FPascalCoinIESEngine := GetECIESPascalCoinCompatibilityEngine;
+  FPascalCoinIESWithCipherParameters := GetIESCipherParameters;
+
+end;
+
+end.

+ 100 - 0
src/core/UPCEncryption.pas

@@ -0,0 +1,100 @@
+unit UPCEncryption;
+
+{ Copyright (c) 2019 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  This unit contains code made by Ugochukwu Mmaduekwe (aka Xor-el at GitHub)
+  https://github.com/PascalCoin/PascalCoinTools/tree/master/Tools/PascalCoinKeyTool
+  Available under MIT License
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{
+  This unit is used to call Encryption/Decryption routines used by PascalCoin
+  - PascalCoin ECDSA encryption
+  - PascalCoin AES encryption
+
+  It will use OpenSSL library or native CryptoLib4Pascal based on config.inc file
+}
+
+interface
+
+{$I config.inc}
+
+{$IF (not Defined(Use_OpenSSL)) and (not Defined(Use_CryptoLib4Pascal))}
+  {$Message Fatal 'ERROR: Use_OpenSSL or Use_CryptoLib4Pascal are not defined, you need to at least define one!'}
+  ERROR: Use_OpenSSL or Use_CryptoLib4Pascal are not defined, you need to at least define one!
+{$ENDIF}
+
+Uses SysUtils, UBaseTypes,
+  {$IFDEF Use_OpenSSL}
+  UAES, UECIES,
+  {$ENDIF}
+  {$IFDEF Use_CryptoLib4Pascal}
+  UPCCryptoLib4Pascal,
+  {$ENDIF}
+  UPCDataTypes;
+
+type
+  TPCEncryption = Class
+  public
+    class function DoPascalCoinECIESEncrypt(const APublicKey : TECDSA_Public; const AMessage : TBytes; var AEncryptedMessage : TBytes) : Boolean;
+    class function DoPascalCoinECIESDecrypt(const APrivateKeyInfo : TECPrivateKeyInfo; const AEncryptedMessage : TBytes; var ADecryptedMessage : TBytes) : Boolean;
+    class function DoPascalCoinAESEncrypt(const AMessage, APassword: TBytes): TBytes; static;
+    class function DoPascalCoinAESDecrypt(const AEncryptedMessage, APassword: TBytes; out ADecryptedMessage: TBytes): boolean; static;
+  End;
+
+implementation
+
+
+{ TPCEncryption }
+
+class function TPCEncryption.DoPascalCoinAESDecrypt(const AEncryptedMessage, APassword: TBytes; out ADecryptedMessage: TBytes): boolean;
+begin
+  {$IFDEF Use_OpenSSL}
+  Result := TAESComp.EVP_Decrypt_AES256(AEncryptedMessage,APassword,ADecryptedMessage);
+  {$ELSE}
+  Result := TPCCryptoLib4Pascal.DoPascalCoinAESDecrypt(AEncryptedMessage,APassword,ADecryptedMessage);
+  {$ENDIF}
+end;
+
+class function TPCEncryption.DoPascalCoinAESEncrypt(const AMessage, APassword: TBytes): TBytes;
+begin
+  {$IFDEF Use_OpenSSL}
+  Result := TAESComp.EVP_Encrypt_AES256(AMessage,APassword);
+  {$ELSE}
+  Result := TPCCryptoLib4Pascal.DoPascalCoinAESEncrypt(AMessage,APassword);
+  {$ENDIF}
+end;
+
+class function TPCEncryption.DoPascalCoinECIESDecrypt(const APrivateKeyInfo : TECPrivateKeyInfo; const AEncryptedMessage: TBytes; var ADecryptedMessage: TBytes): Boolean;
+begin
+  {$IFDEF Use_OpenSSL}
+  Result := ECIESDecrypt(APrivateKeyInfo.EC_OpenSSL_NID,APrivateKeyInfo.RAW_PrivKey,False,AEncryptedMessage,ADecryptedMessage);
+  {$ELSE}
+  Result := TPCCryptoLib4Pascal.DoPascalCoinECIESDecrypt(APrivateKeyInfo.EC_OpenSSL_NID,APrivateKeyInfo.RAW_PrivKey,AEncryptedMessage,ADecryptedMessage);
+  {$ENDIF}
+end;
+
+class function TPCEncryption.DoPascalCoinECIESEncrypt(const APublicKey: TECDSA_Public; const AMessage: TBytes; var AEncryptedMessage: TBytes): Boolean;
+begin
+  {$IFDEF Use_OpenSSL}
+  AEncryptedMessage := ECIESEncrypt(APublicKey,AMessage);
+  Result := True;
+  {$ELSE}
+  Result := TPCCryptoLib4Pascal.DoPascalCoinECIESEncrypt(APublicKey,AMessage,AEncryptedMessage);
+  {$ENDIF}
+end;
+
+end.

+ 127 - 0
src/core/UPCOpenSSLSignature.pas

@@ -0,0 +1,127 @@
+unit UPCOpenSSLSignature;
+
+{ Copyright (c) 2019 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  This unit contains code made by Ugochukwu Mmaduekwe (aka Xor-el at GitHub)
+  https://github.com/PascalCoin/PascalCoinTools/tree/master/Tools/PascalCoinKeyTool
+  Available under MIT License
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{
+  This unit is used to call ECIES Signature and Verify when using OpenSSL
+}
+
+interface
+
+{$I config.inc}
+
+{$IF (not Defined(Use_OpenSSL))}
+  {$Message Fatal 'ERROR: Use_OpenSSL is not defined!'}
+  ERROR: Use_OpenSSL is not defined!
+{$ENDIF}
+
+Uses SysUtils, UBaseTypes,
+  UOpenSSL,
+  UPCDataTypes;
+
+type
+  TPCOpenSSLSignature = Class
+  public
+    class function DoECDSASign(const AEC_OpenSSL_NID : Word; APEC_KEY : PEC_KEY; const AMessage: TBytes; var ASignature : TECDSA_SIG) : Boolean;
+    class function DoECDSAVerify(const APublicKey : TECDSA_Public; const AMessage: TBytes; const ASignature : TECDSA_SIG) : Boolean;
+  End;
+
+implementation
+
+{ TPCOpenSSLSignature }
+
+class function TPCOpenSSLSignature.DoECDSASign(const AEC_OpenSSL_NID: Word; APEC_KEY : PEC_KEY; const AMessage: TBytes; var ASignature: TECDSA_SIG): Boolean;
+Var PECS : PECDSA_SIG;
+  p : PAnsiChar;
+  i : Integer;
+begin
+  PECS := ECDSA_do_sign(PAnsiChar(@AMessage[Low(AMessage)]),Length(AMessage),APEC_KEY);
+  Try
+    if PECS = Nil then raise Exception.Create('Error signing');
+
+    i := BN_num_bytes(PECS^._r);
+    SetLength(ASignature.r,i);
+    p := @ASignature.r[Low(ASignature.r)];
+    i := BN_bn2bin(PECS^._r,p);
+
+    i := BN_num_bytes(PECS^._s);
+    SetLength(ASignature.s,i);
+    p := @ASignature.s[Low(ASignature.s)];
+    i := BN_bn2bin(PECS^._s,p);
+  Finally
+    ECDSA_SIG_free(PECS);
+  End;
+  Result := True;
+end;
+
+class function TPCOpenSSLSignature.DoECDSAVerify(const APublicKey: TECDSA_Public; const AMessage: TBytes; const ASignature: TECDSA_SIG): Boolean;
+Var BNx,BNy : PBIGNUM;
+  ECG : PEC_GROUP;
+  ctx : PBN_CTX;
+  pub_key : PEC_POINT;
+  //
+  PECS : PECDSA_SIG;
+  PK : PEC_KEY;
+  {$IFNDEF OpenSSL10}
+  bnr,bns : PBIGNUM;
+  {$ENDIF}
+begin
+  BNx := BN_bin2bn(PAnsiChar(APublicKey.x),length(APublicKey.x),nil);
+  BNy := BN_bin2bn(PAnsiChar(APublicKey.y),length(APublicKey.y),nil);
+
+  ECG := EC_GROUP_new_by_curve_name(APublicKey.EC_OpenSSL_NID);
+  pub_key := EC_POINT_new(ECG);
+  ctx := BN_CTX_new;
+  if EC_POINT_set_affine_coordinates_GFp(ECG,pub_key,BNx,BNy,ctx)=1 then begin
+    PECS := ECDSA_SIG_new;
+    Try
+      {$IFDEF OpenSSL10}
+      BN_bin2bn(PAnsiChar(Signature.r),Length(Signature.r),PECS^._r);
+      BN_bin2bn(PAnsiChar(Signature.s),Length(Signature.s),PECS^._s);
+      {$ELSE}
+      bnr := BN_bin2bn(PAnsiChar(ASignature.r),Length(ASignature.r),nil);
+      bns := BN_bin2bn(PAnsiChar(ASignature.s),Length(ASignature.s),nil);
+      if ECDSA_SIG_set0(PECS,bnr,bns)<>1 then Raise Exception.Create('Dev error 20161019-1 '+CaptureLastSSLError);
+      {$ENDIF}
+
+      PK := EC_KEY_new_by_curve_name(APublicKey.EC_OpenSSL_NID);
+      EC_KEY_set_public_key(PK,pub_key);
+      Case ECDSA_do_verify(@AMessage[Low(AMessage)],Length(AMessage),PECS,PK) of
+        1 : Result := true;
+        0 : Result := false;
+      Else
+        raise Exception.Create('Error on Verify '+CaptureLastSSLError);
+      End;
+      EC_KEY_free(PK);
+    Finally
+      ECDSA_SIG_free(PECS);
+    End;
+  end else begin
+    Result := false;
+  end;
+  BN_CTX_free(ctx);
+  EC_POINT_free(pub_key);
+  EC_GROUP_free(ECG);
+  BN_free(BNx);
+  BN_free(BNy);
+end;
+
+end.

+ 101 - 0
src/libraries/cryptolib4pascal/ClpAbstractECMultiplier.pas

@@ -0,0 +1,101 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpAbstractECMultiplier;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpBigInteger,
+  ClpIECC,
+  ClpIAbstractECMultiplier,
+  ClpECAlgorithms;
+
+type
+  TAbstractECMultiplier = class abstract(TInterfacedObject,
+    IAbstractECMultiplier, IECMultiplier)
+
+  strict protected
+
+    function CheckResult(const p: IECPoint): IECPoint; virtual;
+    function MultiplyPositive(const p: IECPoint; const k: TBigInteger)
+      : IECPoint; virtual; abstract;
+
+  public
+
+    constructor Create();
+    destructor Destroy; override;
+    function Multiply(const p: IECPoint; const k: TBigInteger)
+      : IECPoint; virtual;
+
+  end;
+
+implementation
+
+{ TAbstractECMultiplier }
+
+function TAbstractECMultiplier.CheckResult(const p: IECPoint): IECPoint;
+begin
+  result := TECAlgorithms.ImplCheckResult(p);
+end;
+
+constructor TAbstractECMultiplier.Create;
+begin
+  Inherited Create();
+end;
+
+destructor TAbstractECMultiplier.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TAbstractECMultiplier.Multiply(const p: IECPoint; const k: TBigInteger)
+  : IECPoint;
+var
+  positive: IECPoint;
+  sign: Int32;
+begin
+
+  sign := k.SignValue;
+  if ((sign = 0) or (p.IsInfinity)) then
+  begin
+    result := p.Curve.Infinity;
+    Exit;
+  end;
+
+  positive := MultiplyPositive(p, k.Abs());
+
+  if sign > 0 then
+  begin
+    result := positive
+  end
+  else
+  begin
+    result := positive.Negate();
+  end;
+
+  // /*
+  // * Although the various multipliers ought not to produce invalid output under normal
+  // * circumstances, a final check here is advised to guard against fault attacks.
+  // */
+  result := CheckResult(result);
+
+end;
+
+end.

+ 792 - 0
src/libraries/cryptolib4pascal/ClpAesEngine.pas

@@ -0,0 +1,792 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpAesEngine;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpIAesEngine,
+  ClpIBlockCipher,
+  ClpICipherParameters,
+  ClpIKeyParameter,
+  ClpCheck,
+  ClpBits,
+  ClpConverters,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SAESEngineNotInitialised = 'AES Engine not Initialised';
+  SInputBuffertooShort = 'Input Buffer too Short';
+  SOutputBuffertooShort = 'Output Buffer too Short';
+  SInvalidParameterAESInit = 'Invalid Parameter Passed to AES Init - "%s"';
+  SInvalidKeyLength = 'Key Length not 128/192/256 bits.';
+  SInvalidOperation = 'Should Never Get Here';
+
+type
+
+  /// <summary>
+  /// <para>
+  /// an implementation of the AES (Rijndael), from FIPS-197.
+  /// </para>
+  /// <para>
+  /// For further details see: <see href="http://csrc.nist.gov/encryption/aes/" />
+  /// </para>
+  /// <para>
+  /// This implementation is based on optimizations from Dr. Brian
+  /// Gladman's paper and C code at <see href="http://fp.gladman.plus.com/cryptography_technology/rijndael/" />
+  /// </para>
+  /// <para>
+  /// This version uses only one 256 word table for each, for a total of
+  /// 2Kbytes, <br />adding 12 rotate operations per round to compute the
+  /// values contained in the other tables from <br />the contents of the
+  /// first.
+  /// </para>
+  /// <para>
+  /// This file contains the middle performance version with 2Kbytes of
+  /// static tables for round precomputation.
+  /// </para>
+  /// </summary>
+  TAesEngine = class sealed(TInterfacedObject, IAesEngine, IBlockCipher)
+
+  strict private
+  const
+    // multiply four bytes in GF(2^8) by 'x' {02} in parallel //
+
+    m1 = UInt32($80808080);
+    m2 = UInt32($7F7F7F7F);
+    m3 = UInt32($0000001B);
+    m4 = UInt32($C0C0C0C0);
+    m5 = UInt32($3F3F3F3F);
+    BLOCK_SIZE = Int32(16);
+
+    // The S box
+    S: array [0 .. 255] of Byte = (99, 124, 119, 123, 242, 107, 111, 197, 48, 1,
+      103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173,
+      212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204,
+      52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7,
+      18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59,
+      214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203,
+      190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2,
+      127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218,
+      33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126,
+      61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184,
+      20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98,
+      145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244,
+      234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116,
+      31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185,
+      134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135,
+      233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45,
+      15, 176, 84, 187, 22);
+
+    // The inverse S-box
+    Si: array [0 .. 255] of Byte = (82, 9, 106, 213, 48, 54, 165, 56, 191, 64,
+      163, 158, 129, 243, 215, 251, 124, 227, 57, 130, 155, 47, 255, 135, 52,
+      142, 67, 68, 196, 222, 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238,
+      76, 149, 11, 66, 250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91,
+      162, 73, 109, 139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212,
+      164, 92, 204, 93, 101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94,
+      21, 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247,
+      228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, 193, 175,
+      189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, 234, 151, 242,
+      207, 206, 240, 180, 230, 115, 150, 172, 116, 34, 231, 173, 53, 133, 226,
+      249, 55, 232, 28, 117, 223, 110, 71, 241, 26, 113, 29, 41, 197, 137, 111,
+      183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, 198, 210, 121, 32, 154,
+      219, 192, 254, 120, 205, 90, 244, 31, 221, 168, 51, 136, 7, 199, 49, 177,
+      18, 16, 89, 39, 128, 236, 95, 96, 81, 127, 169, 25, 181, 74, 13, 45, 229,
+      122, 159, 147, 201, 156, 239, 160, 224, 59, 77, 174, 42, 245, 176, 200,
+      235, 187, 60, 131, 83, 153, 97, 23, 43, 4, 126, 186, 119, 214, 38, 225,
+      105, 20, 99, 85, 33, 12, 125);
+
+    // vector used in calculating key schedule (powers of x in GF(256))
+    Rcon: array [0 .. 29] of Byte = ($01, $02, $04, $08, $10, $20, $40, $80,
+      $1B, $36, $6C, $D8, $AB, $4D, $9A, $2F, $5E, $BC, $63, $C6, $97, $35, $6A,
+      $D4, $B3, $7D, $FA, $EF, $C5, $91);
+
+    // precomputation tables of calculations for rounds
+    T0: array [0 .. 255] of UInt32 = ($A56363C6, $847C7CF8, $997777EE,
+      $8D7B7BF6, $0DF2F2FF, $BD6B6BD6, $B16F6FDE, $54C5C591, $50303060,
+      $03010102, $A96767CE, $7D2B2B56, $19FEFEE7, $62D7D7B5, $E6ABAB4D,
+      $9A7676EC, $45CACA8F, $9D82821F, $40C9C989, $877D7DFA, $15FAFAEF,
+      $EB5959B2, $C947478E, $0BF0F0FB, $ECADAD41, $67D4D4B3, $FDA2A25F,
+      $EAAFAF45, $BF9C9C23, $F7A4A453, $967272E4, $5BC0C09B, $C2B7B775,
+      $1CFDFDE1, $AE93933D, $6A26264C, $5A36366C, $413F3F7E, $02F7F7F5,
+      $4FCCCC83, $5C343468, $F4A5A551, $34E5E5D1, $08F1F1F9, $937171E2,
+      $73D8D8AB, $53313162, $3F15152A, $0C040408, $52C7C795, $65232346,
+      $5EC3C39D, $28181830, $A1969637, $0F05050A, $B59A9A2F, $0907070E,
+      $36121224, $9B80801B, $3DE2E2DF, $26EBEBCD, $6927274E, $CDB2B27F,
+      $9F7575EA, $1B090912, $9E83831D, $742C2C58, $2E1A1A34, $2D1B1B36,
+      $B26E6EDC, $EE5A5AB4, $FBA0A05B, $F65252A4, $4D3B3B76, $61D6D6B7,
+      $CEB3B37D, $7B292952, $3EE3E3DD, $712F2F5E, $97848413, $F55353A6,
+      $68D1D1B9, $00000000, $2CEDEDC1, $60202040, $1FFCFCE3, $C8B1B179,
+      $ED5B5BB6, $BE6A6AD4, $46CBCB8D, $D9BEBE67, $4B393972, $DE4A4A94,
+      $D44C4C98, $E85858B0, $4ACFCF85, $6BD0D0BB, $2AEFEFC5, $E5AAAA4F,
+      $16FBFBED, $C5434386, $D74D4D9A, $55333366, $94858511, $CF45458A,
+      $10F9F9E9, $06020204, $817F7FFE, $F05050A0, $443C3C78, $BA9F9F25,
+      $E3A8A84B, $F35151A2, $FEA3A35D, $C0404080, $8A8F8F05, $AD92923F,
+      $BC9D9D21, $48383870, $04F5F5F1, $DFBCBC63, $C1B6B677, $75DADAAF,
+      $63212142, $30101020, $1AFFFFE5, $0EF3F3FD, $6DD2D2BF, $4CCDCD81,
+      $140C0C18, $35131326, $2FECECC3, $E15F5FBE, $A2979735, $CC444488,
+      $3917172E, $57C4C493, $F2A7A755, $827E7EFC, $473D3D7A, $AC6464C8,
+      $E75D5DBA, $2B191932, $957373E6, $A06060C0, $98818119, $D14F4F9E,
+      $7FDCDCA3, $66222244, $7E2A2A54, $AB90903B, $8388880B, $CA46468C,
+      $29EEEEC7, $D3B8B86B, $3C141428, $79DEDEA7, $E25E5EBC, $1D0B0B16,
+      $76DBDBAD, $3BE0E0DB, $56323264, $4E3A3A74, $1E0A0A14, $DB494992,
+      $0A06060C, $6C242448, $E45C5CB8, $5DC2C29F, $6ED3D3BD, $EFACAC43,
+      $A66262C4, $A8919139, $A4959531, $37E4E4D3, $8B7979F2, $32E7E7D5,
+      $43C8C88B, $5937376E, $B76D6DDA, $8C8D8D01, $64D5D5B1, $D24E4E9C,
+      $E0A9A949, $B46C6CD8, $FA5656AC, $07F4F4F3, $25EAEACF, $AF6565CA,
+      $8E7A7AF4, $E9AEAE47, $18080810, $D5BABA6F, $887878F0, $6F25254A,
+      $722E2E5C, $241C1C38, $F1A6A657, $C7B4B473, $51C6C697, $23E8E8CB,
+      $7CDDDDA1, $9C7474E8, $211F1F3E, $DD4B4B96, $DCBDBD61, $868B8B0D,
+      $858A8A0F, $907070E0, $423E3E7C, $C4B5B571, $AA6666CC, $D8484890,
+      $05030306, $01F6F6F7, $120E0E1C, $A36161C2, $5F35356A, $F95757AE,
+      $D0B9B969, $91868617, $58C1C199, $271D1D3A, $B99E9E27, $38E1E1D9,
+      $13F8F8EB, $B398982B, $33111122, $BB6969D2, $70D9D9A9, $898E8E07,
+      $A7949433, $B69B9B2D, $221E1E3C, $92878715, $20E9E9C9, $49CECE87,
+      $FF5555AA, $78282850, $7ADFDFA5, $8F8C8C03, $F8A1A159, $80898909,
+      $170D0D1A, $DABFBF65, $31E6E6D7, $C6424284, $B86868D0, $C3414182,
+      $B0999929, $772D2D5A, $110F0F1E, $CBB0B07B, $FC5454A8, $D6BBBB6D,
+      $3A16162C);
+
+    Tinv0: array [0 .. 255] of UInt32 = ($50A7F451, $5365417E, $C3A4171A,
+      $965E273A, $CB6BAB3B, $F1459D1F, $AB58FAAC, $9303E34B, $55FA3020,
+      $F66D76AD, $9176CC88, $254C02F5, $FCD7E54F, $D7CB2AC5, $80443526,
+      $8FA362B5, $495AB1DE, $671BBA25, $980EEA45, $E1C0FE5D, $02752FC3,
+      $12F04C81, $A397468D, $C6F9D36B, $E75F8F03, $959C9215, $EB7A6DBF,
+      $DA595295, $2D83BED4, $D3217458, $2969E049, $44C8C98E, $6A89C275,
+      $78798EF4, $6B3E5899, $DD71B927, $B64FE1BE, $17AD88F0, $66AC20C9,
+      $B43ACE7D, $184ADF63, $82311AE5, $60335197, $457F5362, $E07764B1,
+      $84AE6BBB, $1CA081FE, $942B08F9, $58684870, $19FD458F, $876CDE94,
+      $B7F87B52, $23D373AB, $E2024B72, $578F1FE3, $2AAB5566, $0728EBB2,
+      $03C2B52F, $9A7BC586, $A50837D3, $F2872830, $B2A5BF23, $BA6A0302,
+      $5C8216ED, $2B1CCF8A, $92B479A7, $F0F207F3, $A1E2694E, $CDF4DA65,
+      $D5BE0506, $1F6234D1, $8AFEA6C4, $9D532E34, $A055F3A2, $32E18A05,
+      $75EBF6A4, $39EC830B, $AAEF6040, $069F715E, $51106EBD, $F98A213E,
+      $3D06DD96, $AE053EDD, $46BDE64D, $B58D5491, $055DC471, $6FD40604,
+      $FF155060, $24FB9819, $97E9BDD6, $CC434089, $779ED967, $BD42E8B0,
+      $888B8907, $385B19E7, $DBEEC879, $470A7CA1, $E90F427C, $C91E84F8,
+      $00000000, $83868009, $48ED2B32, $AC70111E, $4E725A6C, $FBFF0EFD,
+      $5638850F, $1ED5AE3D, $27392D36, $64D90F0A, $21A65C68, $D1545B9B,
+      $3A2E3624, $B1670A0C, $0FE75793, $D296EEB4, $9E919B1B, $4FC5C080,
+      $A220DC61, $694B775A, $161A121C, $0ABA93E2, $E52AA0C0, $43E0223C,
+      $1D171B12, $0B0D090E, $ADC78BF2, $B9A8B62D, $C8A91E14, $8519F157,
+      $4C0775AF, $BBDD99EE, $FD607FA3, $9F2601F7, $BCF5725C, $C53B6644,
+      $347EFB5B, $7629438B, $DCC623CB, $68FCEDB6, $63F1E4B8, $CADC31D7,
+      $10856342, $40229713, $2011C684, $7D244A85, $F83DBBD2, $1132F9AE,
+      $6DA129C7, $4B2F9E1D, $F330B2DC, $EC52860D, $D0E3C177, $6C16B32B,
+      $99B970A9, $FA489411, $2264E947, $C48CFCA8, $1A3FF0A0, $D82C7D56,
+      $EF903322, $C74E4987, $C1D138D9, $FEA2CA8C, $360BD498, $CF81F5A6,
+      $28DE7AA5, $268EB7DA, $A4BFAD3F, $E49D3A2C, $0D927850, $9BCC5F6A,
+      $62467E54, $C2138DF6, $E8B8D890, $5EF7392E, $F5AFC382, $BE805D9F,
+      $7C93D069, $A92DD56F, $B31225CF, $3B99ACC8, $A77D1810, $6E639CE8,
+      $7BBB3BDB, $097826CD, $F418596E, $01B79AEC, $A89A4F83, $656E95E6,
+      $7EE6FFAA, $08CFBC21, $E6E815EF, $D99BE7BA, $CE366F4A, $D4099FEA,
+      $D67CB029, $AFB2A431, $31233F2A, $3094A5C6, $C066A235, $37BC4E74,
+      $A6CA82FC, $B0D090E0, $15D8A733, $4A9804F1, $F7DAEC41, $0E50CD7F,
+      $2FF69117, $8DD64D76, $4DB0EF43, $544DAACC, $DF0496E4, $E3B5D19E,
+      $1B886A4C, $B81F2CC1, $7F516546, $04EA5E9D, $5D358C01, $737487FA,
+      $2E410BFB, $5A1D67B3, $52D2DB92, $335610E9, $1347D66D, $8C61D79A,
+      $7A0CA137, $8E14F859, $893C13EB, $EE27A9CE, $35C961B7, $EDE51CE1,
+      $3CB1477A, $59DFD29C, $3F73F255, $79CE1418, $BF37C773, $EACDF753,
+      $5BAAFD5F, $146F3DDF, $86DB4478, $81F3AFCA, $3EC468B9, $2C342438,
+      $5F40A3C2, $72C31D16, $0C25E2BC, $8B493C28, $41950DFF, $7101A839,
+      $DEB30C08, $9CE4B4D8, $90C15664, $6184CB7B, $70B632D5, $745C6C48,
+      $4257B8D0);
+
+  var
+    FROUNDS: Int32;
+    FWorkingKey: TCryptoLibMatrixUInt32Array;
+    FC0, FC1, FC2, FC3: UInt32;
+    FforEncryption: Boolean;
+
+    Fstate: array [0 .. 255] of Byte;
+
+    function GetAlgorithmName: String; virtual;
+    function GetIsPartialBlockOkay: Boolean; virtual;
+    function GetBlockSize(): Int32; virtual;
+
+    /// <summary>
+    /// <para>
+    /// Calculate the necessary round keys
+    /// </para>
+    /// <para>
+    /// The number of calculations depends on key size and block size
+    /// </para>
+    /// <para>
+    /// AES specified a fixed block size of 128 bits and key sizes
+    /// 128/192/256 bits
+    /// </para>
+    /// <para>
+    /// This code is written assuming those are the only possible values
+    /// </para>
+    /// </summary>
+    function GenerateWorkingKey(forEncryption: Boolean;
+      const key: TCryptoLibByteArray): TCryptoLibMatrixUInt32Array;
+
+    procedure UnPackBlock(const bytes: TCryptoLibByteArray; off: Int32); inline;
+    procedure PackBlock(const bytes: TCryptoLibByteArray; off: Int32); inline;
+
+    procedure EncryptBlock(const KW: TCryptoLibMatrixUInt32Array);
+
+    procedure DecryptBlock(const KW: TCryptoLibMatrixUInt32Array);
+
+    class function Shift(r: UInt32; Shift: Int32): UInt32; static; inline;
+    class function FFmulX(x: UInt32): UInt32; static; inline;
+    class function FFmulX2(x: UInt32): UInt32; static; inline;
+
+    // The following defines provide alternative definitions of FFmulX that might
+    // give improved performance if a fast 32-bit multiply is not available.
+    //
+    // private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
+    // private static final int  m4 = 0x1b1b1b1b;
+    // private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
+
+    class function Inv_Mcol(x: UInt32): UInt32; static; inline;
+    class function SubWord(x: UInt32): UInt32; static; inline;
+
+  public
+
+    /// <summary>
+    /// initialise an AES cipher.
+    /// </summary>
+    /// <param name="forEncryption">
+    /// whether or not we are for encryption.
+    /// </param>
+    /// <param name="parameters">
+    /// the parameters required to set up the cipher.
+    /// </param>
+    /// <exception cref="EArgumentCryptoLibException">
+    /// if the parameters argument is inappropriate.
+    /// </exception>
+    procedure Init(forEncryption: Boolean;
+      const parameters: ICipherParameters); virtual;
+
+    function ProcessBlock(const input: TCryptoLibByteArray; inOff: Int32;
+      const output: TCryptoLibByteArray; outOff: Int32): Int32; virtual;
+
+    procedure Reset(); virtual;
+
+    property AlgorithmName: String read GetAlgorithmName;
+    property IsPartialBlockOkay: Boolean read GetIsPartialBlockOkay;
+  end;
+
+implementation
+
+{ TAesEngine }
+
+class function TAesEngine.Shift(r: UInt32; Shift: Int32): UInt32;
+begin
+  result := TBits.RotateRight32(r, Shift);
+end;
+
+class function TAesEngine.SubWord(x: UInt32): UInt32;
+begin
+  result := UInt32(S[x and 255]) or (UInt32(S[(x shr 8) and 255]) shl 8) or
+    (UInt32(S[(x shr 16) and 255]) shl 16) or
+    (UInt32(S[(x shr 24) and 255]) shl 24);
+end;
+
+procedure TAesEngine.DecryptBlock(const KW: TCryptoLibMatrixUInt32Array);
+var
+  lkw: TCryptoLibUInt32Array;
+  lt0, lt1, lt2, lr0, lr1, lr2, lr3: UInt32;
+  lr: Int32;
+begin
+  lkw := KW[FROUNDS];
+  lt0 := FC0 xor lkw[0];
+  lt1 := FC1 xor lkw[1];
+  lt2 := FC2 xor lkw[2];
+
+  lr3 := FC3 xor lkw[3];
+  lr := FROUNDS - 1;
+  while (lr > 1) do
+  begin
+    lkw := KW[lr];
+    System.Dec(lr);
+    lr0 := Tinv0[lt0 and 255] xor Shift(Tinv0[(lr3 shr 8) and 255], 24)
+      xor Shift(Tinv0[(lt2 shr 16) and 255], 16)
+      xor Shift(Tinv0[(lt1 shr 24) and 255], 8) xor lkw[0];
+    lr1 := Tinv0[lt1 and 255] xor Shift(Tinv0[(lt0 shr 8) and 255], 24)
+      xor Shift(Tinv0[(lr3 shr 16) and 255], 16)
+      xor Shift(Tinv0[(lt2 shr 24) and 255], 8) xor lkw[1];
+    lr2 := Tinv0[lt2 and 255] xor Shift(Tinv0[(lt1 shr 8) and 255], 24)
+      xor Shift(Tinv0[(lt0 shr 16) and 255], 16)
+      xor Shift(Tinv0[(lr3 shr 24) and 255], 8) xor lkw[2];
+    lr3 := Tinv0[lr3 and 255] xor Shift(Tinv0[(lt2 shr 8) and 255], 24)
+      xor Shift(Tinv0[(lt1 shr 16) and 255], 16)
+      xor Shift(Tinv0[(lt0 shr 24) and 255], 8) xor lkw[3];
+    lkw := KW[lr];
+    System.Dec(lr);
+    lt0 := Tinv0[lr0 and 255] xor Shift(Tinv0[(lr3 shr 8) and 255], 24)
+      xor Shift(Tinv0[(lr2 shr 16) and 255], 16)
+      xor Shift(Tinv0[(lr1 shr 24) and 255], 8) xor lkw[0];
+    lt1 := Tinv0[lr1 and 255] xor Shift(Tinv0[(lr0 shr 8) and 255], 24)
+      xor Shift(Tinv0[(lr3 shr 16) and 255], 16)
+      xor Shift(Tinv0[(lr2 shr 24) and 255], 8) xor lkw[1];
+    lt2 := Tinv0[lr2 and 255] xor Shift(Tinv0[(lr1 shr 8) and 255], 24)
+      xor Shift(Tinv0[(lr0 shr 16) and 255], 16)
+      xor Shift(Tinv0[(lr3 shr 24) and 255], 8) xor lkw[2];
+    lr3 := Tinv0[lr3 and 255] xor Shift(Tinv0[(lr2 shr 8) and 255], 24)
+      xor Shift(Tinv0[(lr1 shr 16) and 255], 16)
+      xor Shift(Tinv0[(lr0 shr 24) and 255], 8) xor lkw[3];
+  end;
+
+  lkw := KW[1];
+  lr0 := Tinv0[lt0 and 255] xor Shift(Tinv0[(lr3 shr 8) and 255], 24)
+    xor Shift(Tinv0[(lt2 shr 16) and 255], 16)
+    xor Shift(Tinv0[(lt1 shr 24) and 255], 8) xor lkw[0];
+  lr1 := Tinv0[lt1 and 255] xor Shift(Tinv0[(lt0 shr 8) and 255], 24)
+    xor Shift(Tinv0[(lr3 shr 16) and 255], 16)
+    xor Shift(Tinv0[(lt2 shr 24) and 255], 8) xor lkw[1];
+  lr2 := Tinv0[lt2 and 255] xor Shift(Tinv0[(lt1 shr 8) and 255], 24)
+    xor Shift(Tinv0[(lt0 shr 16) and 255], 16)
+    xor Shift(Tinv0[(lr3 shr 24) and 255], 8) xor lkw[2];
+  lr3 := Tinv0[lr3 and 255] xor Shift(Tinv0[(lt2 shr 8) and 255], 24)
+    xor Shift(Tinv0[(lt1 shr 16) and 255], 16)
+    xor Shift(Tinv0[(lt0 shr 24) and 255], 8) xor lkw[3];
+
+  // the final round's table is a simple function of Si so we don't use a whole other four tables for it
+
+  lkw := KW[0];
+  FC0 := UInt32(Si[lr0 and 255]) xor ((UInt32(Fstate[(lr3 shr 8) and 255]))
+    shl 8) xor ((UInt32(Fstate[(lr2 shr 16) and 255])) shl 16)
+    xor ((UInt32(Si[(lr1 shr 24) and 255])) shl 24) xor lkw[0];
+  FC1 := UInt32(Fstate[lr1 and 255]) xor ((UInt32(Fstate[(lr0 shr 8) and 255]))
+    shl 8) xor ((UInt32(Si[(lr3 shr 16) and 255])) shl 16)
+    xor ((UInt32(Fstate[(lr2 shr 24) and 255])) shl 24) xor lkw[1];
+  FC2 := UInt32(Fstate[lr2 and 255]) xor ((UInt32(Si[(lr1 shr 8) and 255]))
+    shl 8) xor ((UInt32(Si[(lr0 shr 16) and 255])) shl 16)
+    xor ((UInt32(Fstate[(lr3 shr 24) and 255])) shl 24) xor lkw[2];
+  FC3 := UInt32(Si[lr3 and 255]) xor ((UInt32(Fstate[(lr2 shr 8) and 255]))
+    shl 8) xor ((UInt32(Fstate[(lr1 shr 16) and 255])) shl 16)
+    xor ((UInt32(Fstate[(lr0 shr 24) and 255])) shl 24) xor lkw[3];
+
+end;
+
+procedure TAesEngine.EncryptBlock(const KW: TCryptoLibMatrixUInt32Array);
+var
+  lkw: TCryptoLibUInt32Array;
+  lt0, lt1, lt2, lr0, lr1, lr2, lr3: UInt32;
+  lr: Int32;
+begin
+  lkw := KW[0];
+  lt0 := FC0 xor lkw[0];
+  lt1 := FC1 xor lkw[1];
+  lt2 := FC2 xor lkw[2];
+
+  lr3 := FC3 xor lkw[3];
+  lr := 1;
+  while (lr < (FROUNDS - 1)) do
+  begin
+    lkw := KW[lr];
+    System.Inc(lr);
+    lr0 := T0[lt0 and 255] xor Shift(T0[(lt1 shr 8) and 255], 24)
+      xor Shift(T0[(lt2 shr 16) and 255], 16)
+      xor Shift(T0[(lr3 shr 24) and 255], 8) xor lkw[0];
+    lr1 := T0[lt1 and 255] xor Shift(T0[(lt2 shr 8) and 255], 24)
+      xor Shift(T0[(lr3 shr 16) and 255], 16)
+      xor Shift(T0[(lt0 shr 24) and 255], 8) xor lkw[1];
+    lr2 := T0[lt2 and 255] xor Shift(T0[(lr3 shr 8) and 255], 24)
+      xor Shift(T0[(lt0 shr 16) and 255], 16)
+      xor Shift(T0[(lt1 shr 24) and 255], 8) xor lkw[2];
+    lr3 := T0[lr3 and 255] xor Shift(T0[(lt0 shr 8) and 255], 24)
+      xor Shift(T0[(lt1 shr 16) and 255], 16)
+      xor Shift(T0[(lt2 shr 24) and 255], 8) xor lkw[3];
+    lkw := KW[lr];
+    System.Inc(lr);
+    lt0 := T0[lr0 and 255] xor Shift(T0[(lr1 shr 8) and 255], 24)
+      xor Shift(T0[(lr2 shr 16) and 255], 16)
+      xor Shift(T0[(lr3 shr 24) and 255], 8) xor lkw[0];
+    lt1 := T0[lr1 and 255] xor Shift(T0[(lr2 shr 8) and 255], 24)
+      xor Shift(T0[(lr3 shr 16) and 255], 16)
+      xor Shift(T0[(lr0 shr 24) and 255], 8) xor lkw[1];
+    lt2 := T0[lr2 and 255] xor Shift(T0[(lr3 shr 8) and 255], 24)
+      xor Shift(T0[(lr0 shr 16) and 255], 16)
+      xor Shift(T0[(lr1 shr 24) and 255], 8) xor lkw[2];
+    lr3 := T0[lr3 and 255] xor Shift(T0[(lr0 shr 8) and 255], 24)
+      xor Shift(T0[(lr1 shr 16) and 255], 16)
+      xor Shift(T0[(lr2 shr 24) and 255], 8) xor lkw[3];
+  end;
+
+  lkw := KW[lr];
+  System.Inc(lr);
+  lr0 := T0[lt0 and 255] xor Shift(T0[(lt1 shr 8) and 255], 24)
+    xor Shift(T0[(lt2 shr 16) and 255], 16) xor Shift(T0[(lr3 shr 24) and 255],
+    8) xor lkw[0];
+  lr1 := T0[lt1 and 255] xor Shift(T0[(lt2 shr 8) and 255], 24)
+    xor Shift(T0[(lr3 shr 16) and 255], 16) xor Shift(T0[(lt0 shr 24) and 255],
+    8) xor lkw[1];
+  lr2 := T0[lt2 and 255] xor Shift(T0[(lr3 shr 8) and 255], 24)
+    xor Shift(T0[(lt0 shr 16) and 255], 16) xor Shift(T0[(lt1 shr 24) and 255],
+    8) xor lkw[2];
+  lr3 := T0[lr3 and 255] xor Shift(T0[(lt0 shr 8) and 255], 24)
+    xor Shift(T0[(lt1 shr 16) and 255], 16) xor Shift(T0[(lt2 shr 24) and 255],
+    8) xor lkw[3];
+
+  // the final round's table is a simple function of S so we don't use a whole other four tables for it
+
+  lkw := KW[lr];
+  FC0 := UInt32(S[lr0 and 255]) xor ((UInt32(S[(lr1 shr 8) and 255])) shl 8)
+    xor ((UInt32(Fstate[(lr2 shr 16) and 255])) shl 16)
+    xor ((UInt32(Fstate[(lr3 shr 24) and 255])) shl 24) xor lkw[0];
+  FC1 := UInt32(Fstate[lr1 and 255]) xor ((UInt32(S[(lr2 shr 8) and 255]))
+    shl 8) xor ((UInt32(S[(lr3 shr 16) and 255])) shl 16)
+    xor ((UInt32(Fstate[(lr0 shr 24) and 255])) shl 24) xor lkw[1];
+  FC2 := UInt32(Fstate[lr2 and 255]) xor ((UInt32(S[(lr3 shr 8) and 255]))
+    shl 8) xor ((UInt32(S[(lr0 shr 16) and 255])) shl 16)
+    xor ((UInt32(S[(lr1 shr 24) and 255])) shl 24) xor lkw[2];
+  FC3 := UInt32(Fstate[lr3 and 255]) xor ((UInt32(Fstate[(lr0 shr 8) and 255]))
+    shl 8) xor ((UInt32(Fstate[(lr1 shr 16) and 255])) shl 16)
+    xor ((UInt32(S[(lr2 shr 24) and 255])) shl 24) xor lkw[3];
+
+end;
+
+class function TAesEngine.FFmulX(x: UInt32): UInt32;
+begin
+  result := ((x and m2) shl 1) xor (((x and m1) shr 7) * m3);
+end;
+
+class function TAesEngine.FFmulX2(x: UInt32): UInt32;
+var
+  lt0, t1: UInt32;
+begin
+  lt0 := (x and m5) shl 2;
+  t1 := (x and m4);
+  t1 := t1 xor (t1 shr 1);
+  result := lt0 xor (t1 shr 2) xor (t1 shr 5);
+end;
+
+class function TAesEngine.Inv_Mcol(x: UInt32): UInt32;
+var
+  lt0, t1: UInt32;
+begin
+  lt0 := x;
+  t1 := lt0 xor Shift(lt0, 8);
+  lt0 := lt0 xor FFmulX(t1);
+  t1 := t1 xor FFmulX2(lt0);
+  lt0 := lt0 xor (t1 xor Shift(t1, 16));
+  result := lt0;
+end;
+
+function TAesEngine.GenerateWorkingKey(forEncryption: Boolean;
+  const key: TCryptoLibByteArray): TCryptoLibMatrixUInt32Array;
+var
+  keyLen, KC, i, j: Int32;
+  smallw: TCryptoLibUInt32Array;
+  bigW: TCryptoLibMatrixUInt32Array;
+  u, lrcon, lt0, t1, t2, t3, t4, t5, t6, t7: UInt32;
+
+begin
+  keyLen := System.Length(key);
+  if ((keyLen < 16) or (keyLen > 32) or ((keyLen and 7) <> 0)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidKeyLength);
+  end;
+
+  KC := keyLen shr 2; // KC := keyLen div 4;
+  FROUNDS := KC + 6;
+  // This is not always true for the generalized Rijndael that allows larger block sizes
+  System.SetLength(bigW, FROUNDS + 1); // 4 words in a block
+
+  for i := 0 to FROUNDS do
+  begin
+    System.SetLength(bigW[i], 4);
+  end;
+
+  case KC of
+    4:
+      begin
+        lt0 := TConverters.ReadBytesAsUInt32LE(PByte(key), 0);
+        bigW[0][0] := lt0;
+        t1 := TConverters.ReadBytesAsUInt32LE(PByte(key), 4);
+        bigW[0][1] := t1;
+        t2 := TConverters.ReadBytesAsUInt32LE(PByte(key), 8);
+        bigW[0][2] := t2;
+        t3 := TConverters.ReadBytesAsUInt32LE(PByte(key), 12);
+        bigW[0][3] := t3;
+
+        for i := 1 to 10 do
+        begin
+          u := SubWord(Shift(t3, 8)) xor Rcon[i - 1];
+          lt0 := lt0 xor u;
+          bigW[i][0] := lt0;
+          t1 := t1 xor lt0;
+          bigW[i][1] := t1;
+          t2 := t2 xor t1;
+          bigW[i][2] := t2;
+          t3 := t3 xor t2;
+          bigW[i][3] := t3;
+        end;
+      end;
+
+    6:
+      begin
+        lt0 := TConverters.ReadBytesAsUInt32LE(PByte(key), 0);
+        bigW[0][0] := lt0;
+        t1 := TConverters.ReadBytesAsUInt32LE(PByte(key), 4);
+        bigW[0][1] := t1;
+        t2 := TConverters.ReadBytesAsUInt32LE(PByte(key), 8);
+        bigW[0][2] := t2;
+        t3 := TConverters.ReadBytesAsUInt32LE(PByte(key), 12);
+        bigW[0][3] := t3;
+        t4 := TConverters.ReadBytesAsUInt32LE(PByte(key), 16);
+        bigW[1][0] := t4;
+        t5 := TConverters.ReadBytesAsUInt32LE(PByte(key), 20);
+        bigW[1][1] := t5;
+
+        lrcon := 1;
+        u := SubWord(Shift(t5, 8)) xor lrcon;
+        lrcon := lrcon shl 1;
+        lt0 := lt0 xor u;
+        bigW[1][2] := lt0;
+        t1 := t1 xor lt0;
+        bigW[1][3] := t1;
+        t2 := t2 xor t1;
+        bigW[2][0] := t2;
+        t3 := t3 xor t2;
+        bigW[2][1] := t3;
+        t4 := t4 xor t3;
+        bigW[2][2] := t4;
+        t5 := t5 xor t4;
+        bigW[2][3] := t5;
+
+        i := 3;
+
+        while i < 12 do
+
+        begin
+          u := SubWord(Shift(t5, 8)) xor lrcon;
+          lrcon := lrcon shl 1;
+          lt0 := lt0 xor u;
+          bigW[i][0] := lt0;
+          t1 := t1 xor lt0;
+          bigW[i][1] := t1;
+          t2 := t2 xor t1;
+          bigW[i][2] := t2;
+          t3 := t3 xor t2;
+          bigW[i][3] := t3;
+          t4 := t4 xor t3;
+          bigW[i + 1][0] := t4;
+          t5 := t5 xor t4;
+          bigW[i + 1][1] := t5;
+          u := SubWord(Shift(t5, 8)) xor lrcon;
+          lrcon := lrcon shl 1;
+          lt0 := lt0 xor u;
+          bigW[i + 1][2] := lt0;
+          t1 := t1 xor lt0;
+          bigW[i + 1][3] := t1;
+          t2 := t2 xor t1;
+          bigW[i + 2][0] := t2;
+          t3 := t3 xor t2;
+          bigW[i + 2][1] := t3;
+          t4 := t4 xor t3;
+          bigW[i + 2][2] := t4;
+          t5 := t5 xor t4;
+          bigW[i + 2][3] := t5;
+          System.Inc(i, 3);
+        end;
+
+        u := SubWord(Shift(t5, 8)) xor lrcon;
+        lt0 := lt0 xor u;
+        bigW[12][0] := lt0;
+        t1 := t1 xor lt0;
+        bigW[12][1] := t1;
+        t2 := t2 xor t1;
+        bigW[12][2] := t2;
+        t3 := t3 xor t2;
+        bigW[12][3] := t3;
+      end;
+
+    8:
+      begin
+        lt0 := TConverters.ReadBytesAsUInt32LE(PByte(key), 0);
+        bigW[0][0] := lt0;
+        t1 := TConverters.ReadBytesAsUInt32LE(PByte(key), 4);
+        bigW[0][1] := t1;
+        t2 := TConverters.ReadBytesAsUInt32LE(PByte(key), 8);
+        bigW[0][2] := t2;
+        t3 := TConverters.ReadBytesAsUInt32LE(PByte(key), 12);
+        bigW[0][3] := t3;
+        t4 := TConverters.ReadBytesAsUInt32LE(PByte(key), 16);
+        bigW[1][0] := t4;
+        t5 := TConverters.ReadBytesAsUInt32LE(PByte(key), 20);
+        bigW[1][1] := t5;
+        t6 := TConverters.ReadBytesAsUInt32LE(PByte(key), 24);
+        bigW[1][2] := t6;
+        t7 := TConverters.ReadBytesAsUInt32LE(PByte(key), 28);
+        bigW[1][3] := t7;
+
+        lrcon := 1;
+
+        i := 2;
+
+        while i < 14 do
+
+        begin
+          u := SubWord(Shift(t7, 8)) xor lrcon;
+          lrcon := lrcon shl 1;
+          lt0 := lt0 xor u;
+          bigW[i][0] := lt0;
+          t1 := t1 xor lt0;
+          bigW[i][1] := t1;
+          t2 := t2 xor t1;
+          bigW[i][2] := t2;
+          t3 := t3 xor t2;;
+          bigW[i][3] := t3;
+          u := SubWord(t3);
+          t4 := t4 xor u;
+          bigW[i + 1][0] := t4;
+          t5 := t5 xor t4;
+          bigW[i + 1][1] := t5;
+          t6 := t6 xor t5;
+          bigW[i + 1][2] := t6;
+          t7 := t7 xor t6;
+          bigW[i + 1][3] := t7;
+          System.Inc(i, 2);
+        end;
+
+        u := SubWord(Shift(t7, 8)) xor lrcon;
+        lt0 := lt0 xor u;
+        bigW[14][0] := lt0;
+        t1 := t1 xor lt0;
+        bigW[14][1] := t1;
+        t2 := t2 xor t1;
+        bigW[14][2] := t2;
+        t3 := t3 xor t2;;
+        bigW[14][3] := t3;
+      end
+  else
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes(@SInvalidOperation);
+    end;
+  end;
+
+  if (not forEncryption) then
+  begin
+    for j := 1 to System.Pred(FROUNDS) do
+
+    begin
+      smallw := bigW[j];
+      for i := 0 to System.Pred(4) do
+
+      begin
+        smallw[i] := Inv_Mcol(smallw[i]);
+      end;
+    end;
+  end;
+
+  result := bigW;
+
+end;
+
+function TAesEngine.GetAlgorithmName: String;
+begin
+  result := 'AES';
+end;
+
+function TAesEngine.GetBlockSize: Int32;
+begin
+  result := BLOCK_SIZE;
+end;
+
+function TAesEngine.GetIsPartialBlockOkay: Boolean;
+begin
+  result := false;
+end;
+
+procedure TAesEngine.Init(forEncryption: Boolean;
+  const parameters: ICipherParameters);
+var
+  keyParameter: IKeyParameter;
+begin
+
+  if not Supports(parameters, IKeyParameter, keyParameter) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SInvalidParameterAESInit,
+      [(parameters as TObject).ToString]);
+  end;
+
+  FWorkingKey := GenerateWorkingKey(forEncryption, keyParameter.GetKey());
+
+  FforEncryption := forEncryption;
+
+  if forEncryption then
+  begin
+    System.Move(S[System.Low(S)], Fstate[System.Low(Fstate)], System.SizeOf(S));
+  end
+  else
+  begin
+    System.Move(Si[System.Low(Si)], Fstate[System.Low(Fstate)],
+      System.SizeOf(Si));
+  end;
+
+end;
+
+procedure TAesEngine.PackBlock(const bytes: TCryptoLibByteArray; off: Int32);
+begin
+  TConverters.ReadUInt32AsBytesLE(FC0, bytes, off);
+  TConverters.ReadUInt32AsBytesLE(FC1, bytes, off + 4);
+  TConverters.ReadUInt32AsBytesLE(FC2, bytes, off + 8);
+  TConverters.ReadUInt32AsBytesLE(FC3, bytes, off + 12);
+end;
+
+procedure TAesEngine.UnPackBlock(const bytes: TCryptoLibByteArray; off: Int32);
+begin
+  FC0 := TConverters.ReadBytesAsUInt32LE(PByte(bytes), off);
+  FC1 := TConverters.ReadBytesAsUInt32LE(PByte(bytes), off + 4);
+  FC2 := TConverters.ReadBytesAsUInt32LE(PByte(bytes), off + 8);
+  FC3 := TConverters.ReadBytesAsUInt32LE(PByte(bytes), off + 12);
+end;
+
+function TAesEngine.ProcessBlock(const input: TCryptoLibByteArray; inOff: Int32;
+  const output: TCryptoLibByteArray; outOff: Int32): Int32;
+begin
+  if (FWorkingKey = Nil) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SAESEngineNotInitialised);
+  end;
+
+  TCheck.DataLength(input, inOff, 16, SInputBuffertooShort);
+  TCheck.OutputLength(output, outOff, 16, SOutputBuffertooShort);
+
+  UnPackBlock(input, inOff);
+
+  if (FforEncryption) then
+  begin
+    EncryptBlock(FWorkingKey);
+  end
+  else
+  begin
+    DecryptBlock(FWorkingKey);
+  end;
+
+  PackBlock(output, outOff);
+
+  result := BLOCK_SIZE;
+end;
+
+procedure TAesEngine.Reset;
+begin
+  // nothing to do.
+end;
+
+end.

+ 709 - 0
src/libraries/cryptolib4pascal/ClpAesLightEngine.pas

@@ -0,0 +1,709 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpAesLightEngine;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpIAesLightEngine,
+  ClpIBlockCipher,
+  ClpICipherParameters,
+  ClpIKeyParameter,
+  ClpCheck,
+  ClpBits,
+  ClpConverters,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SAESEngineNotInitialised = 'AES Engine not Initialised';
+  SInputBuffertooShort = 'Input Buffer too Short';
+  SOutputBuffertooShort = 'Output Buffer too Short';
+  SInvalidParameterAESInit = 'Invalid Parameter Passed to AES Init - "%s"';
+  SInvalidKeyLength = 'Key Length not 128/192/256 bits.';
+  SInvalidOperation = 'Should Never Get Here';
+
+type
+
+  /// <summary>
+  /// <para>
+  /// an implementation of the AES (Rijndael), from FIPS-197.
+  /// </para>
+  /// <para>
+  /// For further details see: <see href="http://csrc.nist.gov/encryption/aes/" />
+  /// </para>
+  /// <para>
+  /// This implementation is based on optimizations from Dr. Brian
+  /// Gladman's paper and C code at <see href="http://fp.gladman.plus.com/cryptography_technology/rijndael/" />
+  /// </para>
+  /// <para>
+  /// This version uses no static tables at all and computes the values
+  /// in each round.
+  /// </para>
+  /// <para>
+  /// This file contains the slowest performance version with no static
+  /// tables for round precomputation, but it has the smallest foot
+  /// print.
+  /// </para>
+  /// </summary>
+  TAesLightEngine = class sealed(TInterfacedObject, IAesLightEngine,
+    IBlockCipher)
+
+  strict private
+
+  const
+    // multiply four bytes in GF(2^8) by 'x' {02} in parallel //
+
+    m1 = UInt32($80808080);
+    m2 = UInt32($7F7F7F7F);
+    m3 = UInt32($0000001B);
+    m4 = UInt32($C0C0C0C0);
+    m5 = UInt32($3F3F3F3F);
+    BLOCK_SIZE = Int32(16);
+
+    // The S box
+    S: array [0 .. 255] of Byte = (99, 124, 119, 123, 242, 107, 111, 197, 48, 1,
+      103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173,
+      212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204,
+      52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7,
+      18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59,
+      214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203,
+      190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2,
+      127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218,
+      33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126,
+      61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184,
+      20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98,
+      145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244,
+      234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116,
+      31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185,
+      134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135,
+      233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45,
+      15, 176, 84, 187, 22);
+
+    // The inverse S-box
+    Si: array [0 .. 255] of Byte = (82, 9, 106, 213, 48, 54, 165, 56, 191, 64,
+      163, 158, 129, 243, 215, 251, 124, 227, 57, 130, 155, 47, 255, 135, 52,
+      142, 67, 68, 196, 222, 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238,
+      76, 149, 11, 66, 250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91,
+      162, 73, 109, 139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212,
+      164, 92, 204, 93, 101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94,
+      21, 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247,
+      228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, 193, 175,
+      189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, 234, 151, 242,
+      207, 206, 240, 180, 230, 115, 150, 172, 116, 34, 231, 173, 53, 133, 226,
+      249, 55, 232, 28, 117, 223, 110, 71, 241, 26, 113, 29, 41, 197, 137, 111,
+      183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, 198, 210, 121, 32, 154,
+      219, 192, 254, 120, 205, 90, 244, 31, 221, 168, 51, 136, 7, 199, 49, 177,
+      18, 16, 89, 39, 128, 236, 95, 96, 81, 127, 169, 25, 181, 74, 13, 45, 229,
+      122, 159, 147, 201, 156, 239, 160, 224, 59, 77, 174, 42, 245, 176, 200,
+      235, 187, 60, 131, 83, 153, 97, 23, 43, 4, 126, 186, 119, 214, 38, 225,
+      105, 20, 99, 85, 33, 12, 125);
+
+    // vector used in calculating key schedule (powers of x in GF(256))
+    Rcon: array [0 .. 29] of Byte = ($01, $02, $04, $08, $10, $20, $40, $80,
+      $1B, $36, $6C, $D8, $AB, $4D, $9A, $2F, $5E, $BC, $63, $C6, $97, $35, $6A,
+      $D4, $B3, $7D, $FA, $EF, $C5, $91);
+
+  var
+    FROUNDS: Int32;
+    FWorkingKey: TCryptoLibMatrixUInt32Array;
+    FC0, FC1, FC2, FC3: UInt32;
+    FforEncryption: Boolean;
+
+    function GetAlgorithmName: String; virtual;
+    function GetIsPartialBlockOkay: Boolean; virtual;
+    function GetBlockSize(): Int32; virtual;
+
+    /// <summary>
+    /// <para>
+    /// Calculate the necessary round keys
+    /// </para>
+    /// <para>
+    /// The number of calculations depends on key size and block size
+    /// </para>
+    /// <para>
+    /// AES specified a fixed block size of 128 bits and key sizes
+    /// 128/192/256 bits
+    /// </para>
+    /// <para>
+    /// This code is written assuming those are the only possible values
+    /// </para>
+    /// </summary>
+    function GenerateWorkingKey(forEncryption: Boolean;
+      const key: TCryptoLibByteArray): TCryptoLibMatrixUInt32Array;
+
+    procedure UnPackBlock(const bytes: TCryptoLibByteArray; off: Int32); inline;
+    procedure PackBlock(const bytes: TCryptoLibByteArray; off: Int32); inline;
+
+    procedure EncryptBlock(const KW: TCryptoLibMatrixUInt32Array);
+
+    procedure DecryptBlock(const KW: TCryptoLibMatrixUInt32Array);
+
+    class function Shift(r: UInt32; Shift: Int32): UInt32; static; inline;
+    class function FFmulX(x: UInt32): UInt32; static; inline;
+    class function FFmulX2(x: UInt32): UInt32; static; inline;
+
+    // The following defines provide alternative definitions of FFmulX that might
+    // give improved performance if a fast 32-bit multiply is not available.
+    //
+    // private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
+    // private static final int  m4 = 0x1b1b1b1b;
+    // private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
+
+    class function Mcol(x: UInt32): UInt32; static; inline;
+    class function Inv_Mcol(x: UInt32): UInt32; static; inline;
+    class function SubWord(x: UInt32): UInt32; static; inline;
+
+  public
+    /// <summary>
+    /// initialise an AES cipher.
+    /// </summary>
+    /// <param name="forEncryption">
+    /// whether or not we are for encryption.
+    /// </param>
+    /// <param name="parameters">
+    /// the parameters required to set up the cipher.
+    /// </param>
+    /// <exception cref="EArgumentCryptoLibException">
+    /// if the parameters argument is inappropriate.
+    /// </exception>
+    procedure Init(forEncryption: Boolean;
+      const parameters: ICipherParameters); virtual;
+
+    function ProcessBlock(const input: TCryptoLibByteArray; inOff: Int32;
+      const output: TCryptoLibByteArray; outOff: Int32): Int32; virtual;
+
+    procedure Reset(); virtual;
+
+    property AlgorithmName: String read GetAlgorithmName;
+    property IsPartialBlockOkay: Boolean read GetIsPartialBlockOkay;
+
+  end;
+
+implementation
+
+{ TAesLightEngine }
+
+class function TAesLightEngine.Shift(r: UInt32; Shift: Int32): UInt32;
+begin
+  result := TBits.RotateRight32(r, Shift);
+end;
+
+class function TAesLightEngine.SubWord(x: UInt32): UInt32;
+begin
+  result := UInt32(S[x and 255]) or (UInt32(S[(x shr 8) and 255]) shl 8) or
+    (UInt32(S[(x shr 16) and 255]) shl 16) or
+    (UInt32(S[(x shr 24) and 255]) shl 24);
+end;
+
+class function TAesLightEngine.FFmulX(x: UInt32): UInt32;
+begin
+  result := ((x and m2) shl 1) xor (((x and m1) shr 7) * m3);
+end;
+
+class function TAesLightEngine.FFmulX2(x: UInt32): UInt32;
+var
+  t0, t1: UInt32;
+begin
+  t0 := (x and m5) shl 2;
+  t1 := (x and m4);
+  t1 := t1 xor (t1 shr 1);
+  result := t0 xor (t1 shr 2) xor (t1 shr 5);
+end;
+
+class function TAesLightEngine.Mcol(x: UInt32): UInt32;
+var
+  t0, t1: UInt32;
+begin
+  t0 := Shift(x, 8);
+  t1 := x xor t0;
+  result := Shift(t1, 16) xor t0 xor FFmulX(t1);
+end;
+
+class function TAesLightEngine.Inv_Mcol(x: UInt32): UInt32;
+var
+  t0, t1: UInt32;
+begin
+  t0 := x;
+  t1 := t0 xor Shift(t0, 8);
+  t0 := t0 xor FFmulX(t1);
+  t1 := t1 xor FFmulX2(t0);
+  t0 := t0 xor (t1 xor Shift(t1, 16));
+  result := t0;
+end;
+
+procedure TAesLightEngine.EncryptBlock(const KW: TCryptoLibMatrixUInt32Array);
+var
+  lkw: TCryptoLibUInt32Array;
+  lt0, lt1, lt2, lr0, lr1, lr2, lr3: UInt32;
+  lr: Int32;
+begin
+  lkw := KW[0];
+  lt0 := FC0 xor lkw[0];
+  lt1 := FC1 xor lkw[1];
+  lt2 := FC2 xor lkw[2];
+
+  lr3 := FC3 xor lkw[3];
+  lr := 1;
+
+  while (lr < FROUNDS - 1) do
+  begin
+    lkw := KW[lr];
+    System.Inc(lr);
+    lr0 := Mcol(UInt32(S[lt0 and 255]) xor ((UInt32(S[(lt1 shr 8) and 255]))
+      shl 8) xor ((UInt32(S[(lt2 shr 16) and 255])) shl 16)
+      xor ((UInt32(S[(lr3 shr 24) and 255])) shl 24)) xor lkw[0];
+    lr1 := Mcol(UInt32(S[lt1 and 255]) xor ((UInt32(S[(lt2 shr 8) and 255]))
+      shl 8) xor ((UInt32(S[(lr3 shr 16) and 255])) shl 16)
+      xor ((UInt32(S[(lt0 shr 24) and 255])) shl 24)) xor lkw[1];
+    lr2 := Mcol(UInt32(S[lt2 and 255]) xor ((UInt32(S[(lr3 shr 8) and 255]))
+      shl 8) xor ((UInt32(S[(lt0 shr 16) and 255])) shl 16)
+      xor ((UInt32(S[(lt1 shr 24) and 255])) shl 24)) xor lkw[2];
+    lr3 := Mcol(UInt32(S[lr3 and 255]) xor ((UInt32(S[(lt0 shr 8) and 255]))
+      shl 8) xor ((UInt32(S[(lt1 shr 16) and 255])) shl 16)
+      xor ((UInt32(S[(lt2 shr 24) and 255])) shl 24)) xor lkw[3];
+    lkw := KW[lr];
+    System.Inc(lr);
+    lt0 := Mcol(UInt32(S[lr0 and 255]) xor ((UInt32(S[(lr1 shr 8) and 255]))
+      shl 8) xor ((UInt32(S[(lr2 shr 16) and 255])) shl 16)
+      xor ((UInt32(S[(lr3 shr 24) and 255])) shl 24)) xor lkw[0];
+    lt1 := Mcol(UInt32(S[lr1 and 255]) xor ((UInt32(S[(lr2 shr 8) and 255]))
+      shl 8) xor ((UInt32(S[(lr3 shr 16) and 255])) shl 16)
+      xor ((UInt32(S[(lr0 shr 24) and 255])) shl 24)) xor lkw[1];
+    lt2 := Mcol(UInt32(S[lr2 and 255]) xor ((UInt32(S[(lr3 shr 8) and 255]))
+      shl 8) xor ((UInt32(S[(lr0 shr 16) and 255])) shl 16)
+      xor ((UInt32(S[(lr1 shr 24) and 255])) shl 24)) xor lkw[2];
+    lr3 := Mcol(UInt32(S[lr3 and 255]) xor ((UInt32(S[(lr0 shr 8) and 255]))
+      shl 8) xor ((UInt32(S[(lr1 shr 16) and 255])) shl 16)
+      xor ((UInt32(S[(lr2 shr 24) and 255])) shl 24)) xor lkw[3];
+  end;
+
+  lkw := KW[lr];
+  System.Inc(lr);
+  lr0 := Mcol(UInt32(S[lt0 and 255]) xor ((UInt32(S[(lt1 shr 8) and 255]))
+    shl 8) xor ((UInt32(S[(lt2 shr 16) and 255])) shl 16)
+    xor ((UInt32(S[(lr3 shr 24) and 255])) shl 24)) xor lkw[0];
+  lr1 := Mcol(UInt32(S[lt1 and 255]) xor ((UInt32(S[(lt2 shr 8) and 255]))
+    shl 8) xor ((UInt32(S[(lr3 shr 16) and 255])) shl 16)
+    xor ((UInt32(S[(lt0 shr 24) and 255])) shl 24)) xor lkw[1];
+  lr2 := Mcol(UInt32(S[lt2 and 255]) xor ((UInt32(S[(lr3 shr 8) and 255]))
+    shl 8) xor ((UInt32(S[(lt0 shr 16) and 255])) shl 16)
+    xor ((UInt32(S[(lt1 shr 24) and 255])) shl 24)) xor lkw[2];
+  lr3 := Mcol(UInt32(S[lr3 and 255]) xor ((UInt32(S[(lt0 shr 8) and 255]))
+    shl 8) xor ((UInt32(S[(lt1 shr 16) and 255])) shl 16)
+    xor ((UInt32(S[(lt2 shr 24) and 255])) shl 24)) xor lkw[3];
+
+  // the final round is a simple function of S
+
+  lkw := KW[lr];
+  FC0 := UInt32(S[lr0 and 255]) xor ((UInt32(S[(lr1 shr 8) and 255])) shl 8)
+    xor ((UInt32(S[(lr2 shr 16) and 255])) shl 16)
+    xor ((UInt32(S[(lr3 shr 24) and 255])) shl 24) xor lkw[0];
+  FC1 := UInt32(S[lr1 and 255]) xor ((UInt32(S[(lr2 shr 8) and 255])) shl 8)
+    xor ((UInt32(S[(lr3 shr 16) and 255])) shl 16)
+    xor ((UInt32(S[(lr0 shr 24) and 255])) shl 24) xor lkw[1];
+  FC2 := UInt32(S[lr2 and 255]) xor ((UInt32(S[(lr3 shr 8) and 255])) shl 8)
+    xor ((UInt32(S[(lr0 shr 16) and 255])) shl 16)
+    xor ((UInt32(S[(lr1 shr 24) and 255])) shl 24) xor lkw[2];
+  FC3 := UInt32(S[lr3 and 255]) xor ((UInt32(S[(lr0 shr 8) and 255])) shl 8)
+    xor ((UInt32(S[(lr1 shr 16) and 255])) shl 16)
+    xor ((UInt32(S[(lr2 shr 24) and 255])) shl 24) xor lkw[3];
+end;
+
+procedure TAesLightEngine.DecryptBlock(const KW: TCryptoLibMatrixUInt32Array);
+var
+  lkw: TCryptoLibUInt32Array;
+  lt0, lt1, lt2, lr0, lr1, lr2, lr3: UInt32;
+  lr: Int32;
+begin
+  lkw := KW[FROUNDS];
+  lt0 := FC0 xor lkw[0];
+  lt1 := FC1 xor lkw[1];
+  lt2 := FC2 xor lkw[2];
+
+  lr3 := FC3 xor lkw[3];
+  lr := FROUNDS - 1;
+
+  while (lr > 1) do
+  begin
+    lkw := KW[lr];
+    System.Dec(lr);
+    lr0 := Inv_Mcol(UInt32(Si[lt0 and 255])
+      xor ((UInt32(Si[(lr3 shr 8) and 255])) shl 8)
+      xor ((UInt32(Si[(lt2 shr 16) and 255])) shl 16)
+      xor (UInt32(Si[(lt1 shr 24) and 255]) shl 24)) xor lkw[0];
+    lr1 := Inv_Mcol(UInt32(Si[lt1 and 255])
+      xor ((UInt32(Si[(lt0 shr 8) and 255])) shl 8)
+      xor ((UInt32(Si[(lr3 shr 16) and 255])) shl 16)
+      xor (UInt32(Si[(lt2 shr 24) and 255]) shl 24)) xor lkw[1];
+    lr2 := Inv_Mcol(UInt32(Si[lt2 and 255])
+      xor ((UInt32(Si[(lt1 shr 8) and 255])) shl 8)
+      xor ((UInt32(Si[(lt0 shr 16) and 255])) shl 16)
+      xor (UInt32(Si[(lr3 shr 24) and 255]) shl 24)) xor lkw[2];
+    lr3 := Inv_Mcol(UInt32(Si[lr3 and 255])
+      xor ((UInt32(Si[(lt2 shr 8) and 255])) shl 8)
+      xor ((UInt32(Si[(lt1 shr 16) and 255])) shl 16)
+      xor (UInt32(Si[(lt0 shr 24) and 255]) shl 24)) xor lkw[3];
+    lkw := KW[lr];
+    System.Dec(lr);
+    lt0 := Inv_Mcol(UInt32(Si[lr0 and 255])
+      xor ((UInt32(Si[(lr3 shr 8) and 255])) shl 8)
+      xor ((UInt32(Si[(lr2 shr 16) and 255])) shl 16)
+      xor (UInt32(Si[(lr1 shr 24) and 255]) shl 24)) xor lkw[0];
+    lt1 := Inv_Mcol(UInt32(Si[lr1 and 255])
+      xor ((UInt32(Si[(lr0 shr 8) and 255])) shl 8)
+      xor ((UInt32(Si[(lr3 shr 16) and 255])) shl 16)
+      xor (UInt32(Si[(lr2 shr 24) and 255]) shl 24)) xor lkw[1];
+    lt2 := Inv_Mcol(UInt32(Si[lr2 and 255])
+      xor ((UInt32(Si[(lr1 shr 8) and 255])) shl 8)
+      xor ((UInt32(Si[(lr0 shr 16) and 255])) shl 16)
+      xor (UInt32(Si[(lr3 shr 24) and 255]) shl 24)) xor lkw[2];
+    lr3 := Inv_Mcol(UInt32(Si[lr3 and 255])
+      xor ((UInt32(Si[(lr2 shr 8) and 255])) shl 8)
+      xor ((UInt32(Si[(lr1 shr 16) and 255])) shl 16)
+      xor (UInt32(Si[(lr0 shr 24) and 255]) shl 24)) xor lkw[3];
+  end;
+
+  lkw := KW[1];
+  lr0 := Inv_Mcol(UInt32(Si[lt0 and 255]) xor ((UInt32(Si[(lr3 shr 8) and 255]))
+    shl 8) xor ((UInt32(Si[(lt2 shr 16) and 255])) shl 16)
+    xor (UInt32(Si[(lt1 shr 24) and 255]) shl 24)) xor lkw[0];
+  lr1 := Inv_Mcol(UInt32(Si[lt1 and 255]) xor ((UInt32(Si[(lt0 shr 8) and 255]))
+    shl 8) xor ((UInt32(Si[(lr3 shr 16) and 255])) shl 16)
+    xor (UInt32(Si[(lt2 shr 24) and 255]) shl 24)) xor lkw[1];
+  lr2 := Inv_Mcol(UInt32(Si[lt2 and 255]) xor ((UInt32(Si[(lt1 shr 8) and 255]))
+    shl 8) xor ((UInt32(Si[(lt0 shr 16) and 255])) shl 16)
+    xor (UInt32(Si[(lr3 shr 24) and 255]) shl 24)) xor lkw[2];
+  lr3 := Inv_Mcol(UInt32(Si[lr3 and 255]) xor ((UInt32(Si[(lt2 shr 8) and 255]))
+    shl 8) xor ((UInt32(Si[(lt1 shr 16) and 255])) shl 16)
+    xor (UInt32(Si[(lt0 shr 24) and 255]) shl 24)) xor lkw[3];
+
+  // the final round's table is a simple function of Si
+
+  lkw := KW[0];
+  FC0 := UInt32(Si[lr0 and 255]) xor ((UInt32(Si[(lr3 shr 8) and 255])) shl 8)
+    xor ((UInt32(Si[(lr2 shr 16) and 255])) shl 16)
+    xor ((UInt32(Si[(lr1 shr 24) and 255])) shl 24) xor lkw[0];
+  FC1 := UInt32(Si[lr1 and 255]) xor ((UInt32(Si[(lr0 shr 8) and 255])) shl 8)
+    xor ((UInt32(Si[(lr3 shr 16) and 255])) shl 16)
+    xor ((UInt32(Si[(lr2 shr 24) and 255])) shl 24) xor lkw[1];
+  FC2 := UInt32(Si[lr2 and 255]) xor ((UInt32(Si[(lr1 shr 8) and 255])) shl 8)
+    xor ((UInt32(Si[(lr0 shr 16) and 255])) shl 16)
+    xor ((UInt32(Si[(lr3 shr 24) and 255])) shl 24) xor lkw[2];
+  FC3 := UInt32(Si[lr3 and 255]) xor ((UInt32(Si[(lr2 shr 8) and 255])) shl 8)
+    xor ((UInt32(Si[(lr1 shr 16) and 255])) shl 16)
+    xor ((UInt32(Si[(lr0 shr 24) and 255])) shl 24) xor lkw[3];
+end;
+
+function TAesLightEngine.GenerateWorkingKey(forEncryption: Boolean;
+  const key: TCryptoLibByteArray): TCryptoLibMatrixUInt32Array;
+var
+  keyLen, KC, i, j: Int32;
+  smallw: TCryptoLibUInt32Array;
+  bigW: TCryptoLibMatrixUInt32Array;
+  u, lrcon, t0, t1, t2, t3, t4, t5, t6, t7: UInt32;
+
+begin
+  keyLen := System.Length(key);
+  if ((keyLen < 16) or (keyLen > 32) or ((keyLen and 7) <> 0)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidKeyLength);
+  end;
+
+  KC := keyLen shr 2; // KC := keyLen div 4;
+  FROUNDS := KC + 6;
+  // This is not always true for the generalized Rijndael that allows larger block sizes
+  System.SetLength(bigW, FROUNDS + 1); // 4 words in a block
+
+  for i := 0 to FROUNDS do
+  begin
+    System.SetLength(bigW[i], 4);
+  end;
+
+  case KC of
+    4:
+      begin
+        t0 := TConverters.ReadBytesAsUInt32LE(PByte(key), 0);
+        bigW[0][0] := t0;
+        t1 := TConverters.ReadBytesAsUInt32LE(PByte(key), 4);
+        bigW[0][1] := t1;
+        t2 := TConverters.ReadBytesAsUInt32LE(PByte(key), 8);
+        bigW[0][2] := t2;
+        t3 := TConverters.ReadBytesAsUInt32LE(PByte(key), 12);
+        bigW[0][3] := t3;
+
+        for i := 1 to 10 do
+        begin
+          u := SubWord(Shift(t3, 8)) xor Rcon[i - 1];
+          t0 := t0 xor u;
+          bigW[i][0] := t0;
+          t1 := t1 xor t0;
+          bigW[i][1] := t1;
+          t2 := t2 xor t1;
+          bigW[i][2] := t2;
+          t3 := t3 xor t2;
+          bigW[i][3] := t3;
+        end;
+      end;
+
+    6:
+      begin
+        t0 := TConverters.ReadBytesAsUInt32LE(PByte(key), 0);
+        bigW[0][0] := t0;
+        t1 := TConverters.ReadBytesAsUInt32LE(PByte(key), 4);
+        bigW[0][1] := t1;
+        t2 := TConverters.ReadBytesAsUInt32LE(PByte(key), 8);
+        bigW[0][2] := t2;
+        t3 := TConverters.ReadBytesAsUInt32LE(PByte(key), 12);
+        bigW[0][3] := t3;
+        t4 := TConverters.ReadBytesAsUInt32LE(PByte(key), 16);
+        bigW[1][0] := t4;
+        t5 := TConverters.ReadBytesAsUInt32LE(PByte(key), 20);
+        bigW[1][1] := t5;
+
+        lrcon := 1;
+        u := SubWord(Shift(t5, 8)) xor lrcon;
+        lrcon := lrcon shl 1;
+        t0 := t0 xor u;
+        bigW[1][2] := t0;
+        t1 := t1 xor t0;
+        bigW[1][3] := t1;
+        t2 := t2 xor t1;
+        bigW[2][0] := t2;
+        t3 := t3 xor t2;
+        bigW[2][1] := t3;
+        t4 := t4 xor t3;
+        bigW[2][2] := t4;
+        t5 := t5 xor t4;
+        bigW[2][3] := t5;
+
+        i := 3;
+
+        while i < 12 do
+
+        begin
+          u := SubWord(Shift(t5, 8)) xor lrcon;
+          lrcon := lrcon shl 1;
+          t0 := t0 xor u;
+          bigW[i][0] := t0;
+          t1 := t1 xor t0;
+          bigW[i][1] := t1;
+          t2 := t2 xor t1;
+          bigW[i][2] := t2;
+          t3 := t3 xor t2;
+          bigW[i][3] := t3;
+          t4 := t4 xor t3;
+          bigW[i + 1][0] := t4;
+          t5 := t5 xor t4;
+          bigW[i + 1][1] := t5;
+          u := SubWord(Shift(t5, 8)) xor lrcon;
+          lrcon := lrcon shl 1;
+          t0 := t0 xor u;
+          bigW[i + 1][2] := t0;
+          t1 := t1 xor t0;
+          bigW[i + 1][3] := t1;
+          t2 := t2 xor t1;
+          bigW[i + 2][0] := t2;
+          t3 := t3 xor t2;
+          bigW[i + 2][1] := t3;
+          t4 := t4 xor t3;
+          bigW[i + 2][2] := t4;
+          t5 := t5 xor t4;
+          bigW[i + 2][3] := t5;
+          System.Inc(i, 3);
+        end;
+
+        u := SubWord(Shift(t5, 8)) xor lrcon;
+        t0 := t0 xor u;
+        bigW[12][0] := t0;
+        t1 := t1 xor t0;
+        bigW[12][1] := t1;
+        t2 := t2 xor t1;
+        bigW[12][2] := t2;
+        t3 := t3 xor t2;
+        bigW[12][3] := t3;
+      end;
+
+    8:
+      begin
+        t0 := TConverters.ReadBytesAsUInt32LE(PByte(key), 0);
+        bigW[0][0] := t0;
+        t1 := TConverters.ReadBytesAsUInt32LE(PByte(key), 4);
+        bigW[0][1] := t1;
+        t2 := TConverters.ReadBytesAsUInt32LE(PByte(key), 8);
+        bigW[0][2] := t2;
+        t3 := TConverters.ReadBytesAsUInt32LE(PByte(key), 12);
+        bigW[0][3] := t3;
+        t4 := TConverters.ReadBytesAsUInt32LE(PByte(key), 16);
+        bigW[1][0] := t4;
+        t5 := TConverters.ReadBytesAsUInt32LE(PByte(key), 20);
+        bigW[1][1] := t5;
+        t6 := TConverters.ReadBytesAsUInt32LE(PByte(key), 24);
+        bigW[1][2] := t6;
+        t7 := TConverters.ReadBytesAsUInt32LE(PByte(key), 28);
+        bigW[1][3] := t7;
+
+        lrcon := 1;
+
+        i := 2;
+
+        while i < 14 do
+
+        begin
+          u := SubWord(Shift(t7, 8)) xor lrcon;
+          lrcon := lrcon shl 1;
+          t0 := t0 xor u;
+          bigW[i][0] := t0;
+          t1 := t1 xor t0;
+          bigW[i][1] := t1;
+          t2 := t2 xor t1;
+          bigW[i][2] := t2;
+          t3 := t3 xor t2;;
+          bigW[i][3] := t3;
+          u := SubWord(t3);
+          t4 := t4 xor u;
+          bigW[i + 1][0] := t4;
+          t5 := t5 xor t4;
+          bigW[i + 1][1] := t5;
+          t6 := t6 xor t5;
+          bigW[i + 1][2] := t6;
+          t7 := t7 xor t6;
+          bigW[i + 1][3] := t7;
+          System.Inc(i, 2);
+        end;
+
+        u := SubWord(Shift(t7, 8)) xor lrcon;
+        t0 := t0 xor u;
+        bigW[14][0] := t0;
+        t1 := t1 xor t0;
+        bigW[14][1] := t1;
+        t2 := t2 xor t1;
+        bigW[14][2] := t2;
+        t3 := t3 xor t2;;
+        bigW[14][3] := t3;
+      end
+  else
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes(@SInvalidOperation);
+    end;
+  end;
+
+  if (not forEncryption) then
+  begin
+    for j := 1 to System.Pred(FROUNDS) do
+
+    begin
+      smallw := bigW[j];
+      for i := 0 to System.Pred(4) do
+
+      begin
+        smallw[i] := Inv_Mcol(smallw[i]);
+      end;
+    end;
+  end;
+
+  result := bigW;
+
+end;
+
+function TAesLightEngine.GetAlgorithmName: String;
+begin
+  result := 'AES';
+end;
+
+function TAesLightEngine.GetBlockSize: Int32;
+begin
+  result := BLOCK_SIZE;
+end;
+
+function TAesLightEngine.GetIsPartialBlockOkay: Boolean;
+begin
+  result := false;
+end;
+
+procedure TAesLightEngine.Init(forEncryption: Boolean;
+  const parameters: ICipherParameters);
+var
+  keyParameter: IKeyParameter;
+begin
+
+  if not Supports(parameters, IKeyParameter, keyParameter) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SInvalidParameterAESInit,
+      [(parameters as TObject).ToString]);
+  end;
+
+  FWorkingKey := GenerateWorkingKey(forEncryption, keyParameter.GetKey());
+
+  FforEncryption := forEncryption;
+end;
+
+procedure TAesLightEngine.PackBlock(const bytes: TCryptoLibByteArray;
+  off: Int32);
+begin
+  TConverters.ReadUInt32AsBytesLE(FC0, bytes, off);
+  TConverters.ReadUInt32AsBytesLE(FC1, bytes, off + 4);
+  TConverters.ReadUInt32AsBytesLE(FC2, bytes, off + 8);
+  TConverters.ReadUInt32AsBytesLE(FC3, bytes, off + 12);
+end;
+
+procedure TAesLightEngine.UnPackBlock(const bytes: TCryptoLibByteArray;
+  off: Int32);
+begin
+  FC0 := TConverters.ReadBytesAsUInt32LE(PByte(bytes), off);
+  FC1 := TConverters.ReadBytesAsUInt32LE(PByte(bytes), off + 4);
+  FC2 := TConverters.ReadBytesAsUInt32LE(PByte(bytes), off + 8);
+  FC3 := TConverters.ReadBytesAsUInt32LE(PByte(bytes), off + 12);
+end;
+
+function TAesLightEngine.ProcessBlock(const input: TCryptoLibByteArray;
+  inOff: Int32; const output: TCryptoLibByteArray; outOff: Int32): Int32;
+begin
+  if (FWorkingKey = Nil) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SAESEngineNotInitialised);
+  end;
+
+  TCheck.DataLength(input, inOff, 16, SInputBuffertooShort);
+  TCheck.OutputLength(output, outOff, 16, SOutputBuffertooShort);
+
+  UnPackBlock(input, inOff);
+
+  if (FforEncryption) then
+  begin
+    EncryptBlock(FWorkingKey);
+  end
+  else
+  begin
+    DecryptBlock(FWorkingKey);
+  end;
+
+  PackBlock(output, outOff);
+
+  result := BLOCK_SIZE;
+end;
+
+procedure TAesLightEngine.Reset;
+begin
+  // nothing to do.
+end;
+
+end.

+ 423 - 0
src/libraries/cryptolib4pascal/ClpArrayUtils.pas

@@ -0,0 +1,423 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpArrayUtils;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Math,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SInvalidLength = '%d " > " %d';
+
+type
+  TArrayUtils = class sealed(TObject)
+
+  strict private
+    class function GetLength(from, &to: Int32): Int32; static; inline;
+
+  public
+
+    class function Concatenate(const A, B: TCryptoLibStringArray)
+      : TCryptoLibStringArray; overload; static;
+
+    class function Concatenate(const A, B: TCryptoLibByteArray)
+      : TCryptoLibByteArray; overload; static; inline;
+
+    class function Concatenate(const A: TCryptoLibByteArray;
+      const Others: TCryptoLibMatrixByteArray): TCryptoLibByteArray;
+      overload; static;
+
+    class function AreEqual(const A, B: TCryptoLibByteArray): Boolean;
+      overload; static;
+
+    class function AreEqual(const A, B: TCryptoLibInt32Array): Boolean;
+      overload; static;
+
+    class function AreAllZeroes(const buf: TCryptoLibByteArray; off, len: Int32)
+      : Boolean; static;
+
+    class function GetArrayHashCode(const data: TCryptoLibByteArray): Int32;
+      overload; static;
+
+    class function GetArrayHashCode(const data: TCryptoLibInt32Array): Int32;
+      overload; static;
+
+    class function GetArrayHashCode(const data: TCryptoLibUInt32Array): Int32;
+      overload; static;
+
+    class function GetArrayHashCode(const data: TCryptoLibUInt32Array;
+      off, len: Int32): Int32; overload; static;
+
+    class function GetArrayHashCode(const data: TCryptoLibUInt64Array;
+      off, len: Int32): Int32; overload; static;
+
+    class function Prepend(const A: TCryptoLibByteArray; B: Byte)
+      : TCryptoLibByteArray; static;
+
+    class function CopyOf(const data: TCryptoLibByteArray; newLength: Int32)
+      : TCryptoLibByteArray; static;
+
+    class function CopyOfRange(const data: TCryptoLibByteArray;
+      from, &to: Int32): TCryptoLibByteArray; static;
+
+    class function ConstantTimeAreEqual(const a_ar1, a_ar2: TCryptoLibByteArray)
+      : Boolean; static;
+
+    class procedure Fill(const buf: TCryptoLibByteArray; from, &to: Int32;
+      filler: Byte); overload; static;
+
+    class procedure Fill(const buf: TCryptoLibInt32Array; from, &to: Int32;
+      filler: Int32); overload; static;
+
+    class procedure Fill(const buf: TCryptoLibUInt32Array; from, &to: Int32;
+      filler: UInt32); overload; static;
+
+  end;
+
+implementation
+
+{ TArrayUtils }
+
+class function TArrayUtils.GetLength(from, &to: Int32): Int32;
+var
+  newLength: Int32;
+begin
+  newLength := &to - from;
+  if (newLength < 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SInvalidLength,
+      [from, &to]);
+  end;
+  Result := newLength;
+end;
+
+class function TArrayUtils.Concatenate(const A, B: TCryptoLibByteArray)
+  : TCryptoLibByteArray;
+var
+  l: Int32;
+begin
+  l := System.Length(A);
+  System.SetLength(Result, l + System.Length(B));
+  System.Move(A[0], Result[0], l * System.SizeOf(Byte));
+  System.Move(B[0], Result[l], System.Length(B) * System.SizeOf(Byte));
+end;
+
+class function TArrayUtils.Concatenate(const A: TCryptoLibByteArray;
+  const Others: TCryptoLibMatrixByteArray): TCryptoLibByteArray;
+var
+  len, Idx, Pos: Int32;
+  temp: TCryptoLibByteArray;
+begin
+  len := 0;
+  for Idx := System.Low(Others) to System.High(Others) do
+  begin
+    len := len + System.Length(Others[Idx]);
+  end;
+  len := len + System.Length(A);
+  System.SetLength(Result, len);
+  System.Move(A[0], Result[0], System.Length(A) * System.SizeOf(Byte));
+  Pos := System.Length(A);
+  for Idx := System.Low(Others) to System.High(Others) do
+  begin
+    temp := Others[Idx];
+    System.Move(temp[0], Result[Pos], System.Length(temp) *
+      System.SizeOf(Byte));
+    Pos := Pos + System.Length(temp);
+  end;
+end;
+
+class function TArrayUtils.Concatenate(const A, B: TCryptoLibStringArray)
+  : TCryptoLibStringArray;
+var
+  i, l: Int32;
+begin
+  l := System.Length(A);
+  System.SetLength(Result, l + System.Length(B));
+  for i := System.Low(A) to System.High(A) do
+  begin
+    Result[i] := A[i];
+  end;
+
+  for i := System.Low(B) to System.High(B) do
+  begin
+    Result[l + i] := B[i];
+  end;
+end;
+
+class function TArrayUtils.AreEqual(const A, B: TCryptoLibByteArray): Boolean;
+begin
+  if System.Length(A) <> System.Length(B) then
+  begin
+    Result := false;
+    Exit;
+  end;
+
+  Result := CompareMem(A, B, System.Length(A) * System.SizeOf(Byte));
+end;
+
+class function TArrayUtils.AreAllZeroes(const buf: TCryptoLibByteArray;
+  off, len: Int32): Boolean;
+var
+  bits: UInt32;
+  i: Int32;
+begin
+  bits := 0;
+  for i := 0 to System.Pred(len) do
+  begin
+    bits := bits or (buf[off + i]);
+  end;
+  Result := bits = 0;
+end;
+
+class function TArrayUtils.AreEqual(const A, B: TCryptoLibInt32Array): Boolean;
+begin
+  if System.Length(A) <> System.Length(B) then
+  begin
+    Result := false;
+    Exit;
+  end;
+
+  Result := CompareMem(A, B, System.Length(A) * System.SizeOf(Int32));
+end;
+
+{$B+}
+
+class function TArrayUtils.ConstantTimeAreEqual(const a_ar1,
+  a_ar2: TCryptoLibByteArray): Boolean;
+var
+  i: Int32;
+  diff: UInt32;
+
+begin
+  diff := UInt32(System.Length(a_ar1)) xor UInt32(System.Length(a_ar2));
+
+  i := 0;
+
+  while (i <= System.High(a_ar1)) and (i <= System.High(a_ar2)) do
+  begin
+    diff := diff or (UInt32(a_ar1[i] xor a_ar2[i]));
+    System.Inc(i);
+  end;
+
+  Result := diff = 0;
+end;
+
+{$B-}
+
+class function TArrayUtils.CopyOf(const data: TCryptoLibByteArray;
+  newLength: Int32): TCryptoLibByteArray;
+begin
+  System.SetLength(Result, newLength);
+  if (newLength < System.Length(data)) then
+  begin
+    System.Move(data[0], Result[0], newLength * System.SizeOf(Byte));
+  end
+  else
+  begin
+    System.Move(data[0], Result[0], System.Length(data) * System.SizeOf(Byte));
+  end;
+end;
+
+class function TArrayUtils.CopyOfRange(const data: TCryptoLibByteArray;
+  from, &to: Int32): TCryptoLibByteArray;
+var
+  newLength: Int32;
+begin
+  newLength := GetLength(from, &to);
+  System.SetLength(Result, newLength);
+  System.Move(data[from], Result[0], Min(newLength, System.Length(data) - from)
+    * System.SizeOf(Byte));
+end;
+
+class procedure TArrayUtils.Fill(const buf: TCryptoLibByteArray;
+  from, &to: Int32; filler: Byte);
+begin
+  System.FillChar(buf[from], (&to - from) * System.SizeOf(Byte), filler);
+end;
+
+class procedure TArrayUtils.Fill(const buf: TCryptoLibInt32Array;
+  from, &to: Int32; filler: Int32);
+begin
+  while from < &to do
+  begin
+    buf[from] := filler;
+    System.Inc(from);
+  end;
+end;
+
+class procedure TArrayUtils.Fill(const buf: TCryptoLibUInt32Array;
+  from, &to: Int32; filler: UInt32);
+begin
+{$IFDEF FPC}
+  System.FillDWord(buf[from], (&to - from), filler);
+{$ELSE}
+  while from < &to do
+  begin
+    buf[from] := filler;
+    System.Inc(from);
+  end;
+{$ENDIF}
+end;
+
+class function TArrayUtils.GetArrayHashCode(const data
+  : TCryptoLibByteArray): Int32;
+var
+  i, hc: Int32;
+begin
+  if data = Nil then
+  begin
+    Result := 0;
+    Exit;
+  end;
+
+  i := System.Length(data);
+  hc := i + 1;
+
+  System.Dec(i);
+  while (i >= 0) do
+  begin
+    hc := hc * 257;
+    hc := hc xor data[i];
+    System.Dec(i);
+  end;
+  Result := hc;
+end;
+
+class function TArrayUtils.GetArrayHashCode(const data
+  : TCryptoLibInt32Array): Int32;
+var
+  i, hc: Int32;
+begin
+  if data = Nil then
+  begin
+    Result := 0;
+    Exit;
+  end;
+
+  i := System.Length(data);
+  hc := i + 1;
+
+  System.Dec(i);
+  while (i >= 0) do
+  begin
+    hc := hc * 257;
+    hc := hc xor data[i];
+    System.Dec(i);
+  end;
+  Result := hc;
+end;
+
+class function TArrayUtils.GetArrayHashCode(const data
+  : TCryptoLibUInt32Array): Int32;
+var
+  i, hc: Int32;
+begin
+  if data = Nil then
+  begin
+    Result := 0;
+    Exit;
+  end;
+
+  i := System.Length(data);
+  hc := i + 1;
+
+  System.Dec(i);
+  while (i >= 0) do
+  begin
+    hc := hc * 257;
+    hc := hc xor Int32(data[i]);
+    System.Dec(i);
+  end;
+  Result := hc;
+end;
+
+class function TArrayUtils.GetArrayHashCode(const data: TCryptoLibUInt32Array;
+  off, len: Int32): Int32;
+var
+  i, hc: Int32;
+begin
+  if data = Nil then
+  begin
+    Result := 0;
+    Exit;
+  end;
+
+  i := len;
+  hc := i + 1;
+
+  System.Dec(i);
+  while (i >= 0) do
+  begin
+    hc := hc * 257;
+    hc := hc xor Int32(data[off + i]);
+    System.Dec(i);
+  end;
+  Result := hc;
+end;
+
+class function TArrayUtils.GetArrayHashCode(const data: TCryptoLibUInt64Array;
+  off, len: Int32): Int32;
+var
+  i, hc: Int32;
+  di: UInt64;
+begin
+  if data = Nil then
+  begin
+    Result := 0;
+    Exit;
+  end;
+
+  i := len;
+  hc := i + 1;
+
+  System.Dec(i);
+  while (i >= 0) do
+  begin
+    di := data[off + i];
+    hc := hc * 257;
+    hc := hc xor Int32(di);
+    hc := hc * 257;
+    hc := hc xor Int32(di shr 32);
+    System.Dec(i);
+  end;
+  Result := hc;
+end;
+
+class function TArrayUtils.Prepend(const A: TCryptoLibByteArray; B: Byte)
+  : TCryptoLibByteArray;
+var
+  &length: Int32;
+begin
+  if (A = Nil) then
+  begin
+    Result := TCryptoLibByteArray.Create(B);
+    Exit;
+  end;
+
+  Length := System.Length(A);
+  System.SetLength(Result, Length + 1);
+  System.Move(A[0], Result[1], Length * System.SizeOf(Byte));
+  Result[0] := B;
+end;
+
+end.

+ 9987 - 0
src/libraries/cryptolib4pascal/ClpAsn1Objects.pas

@@ -0,0 +1,9987 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpAsn1Objects;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  Math,
+  SyncObjs,
+  StrUtils,
+  SysUtils,
+  Generics.Collections,
+  ClpEncoders,
+  ClpBits,
+  ClpBigInteger,
+  ClpArrayUtils,
+  ClpStringUtils,
+  ClpCryptoLibTypes,
+  ClpConverters,
+  ClpIAsn1Objects,
+  ClpOidTokenizer,
+  ClpIOidTokenizer;
+
+resourcestring
+  SDataOverflow = 'Data Overflow';
+  SCorruptedStreamInvalidTag =
+    'Corrupted Stream - Invalid High Tag Number Found';
+  SEOFFound = 'EOF Found Inside Tag Value';
+  SInvalidEnd = 'EOF Found When Length Expected';
+  SInvalidDerLength = 'DER Length More Than 4 Bytes: %d';
+  SEndOfStream = 'EOF Found Reading Length';
+  SNegativeLength = 'Corrupted Stream - Negative Length Found';
+  SOutOfBoundsLength = 'Corrupted stream - Out of Bounds Length Found';
+  SUnknownTag = 'Unknown Tag " %d " Encountered';
+  SEndOfContent = 'Unexpected End-of-Contents Marker';
+  SIndefiniteLength = 'Indefinite Length Primitive Encoding Encountered';
+  SUnknownBerObject = 'Unknown BER Object Encountered';
+  SCorruptedStream = 'Corrupted Stream Detected: %s';
+  SInvalidLength = 'Negative Lengths not Allowed", "Length"';
+  SEndOfStreamTwo = 'DEF Length  %d " TObject truncated by " %d';
+  SInvalidBufferLength = 'Buffer Length Not Right For Data';
+  SMalformedContent = 'Malformed End-of-Contents Marker';
+
+  SExtraData = 'Extra Data Found After Object';
+  SUnRecognizedObjectStream = 'Cannot Recognise Object in Stream';
+  SUnRecognizedObjectByteArray = 'Cannot Recognise Object in ByteArray';
+  SIllegalObject = 'Illegal Object in GetInstance:  %s, "obj"';
+  SStrNil = '"Str" Cannot be Nil';
+  SProcessingError = 'Error Processing Object : "%s"';
+  SInvalidObject = 'Object Implicit - Explicit Expected.';
+  SUnknownObject = 'Unknown object in GetInstance:  %s, "obj"';
+  SInvalidSequence = '"Failed to Construct Sequence from byte array: " %s';
+  SImplicitObject = 'Implicitly Tagged Object';
+  SImplicitTag = 'Implicit Tagging for Tag:  %d';
+  SUnknownObjectBER = 'Unknown BER Object Encountered: $%x';
+  SImplicitTagging = 'Implicit Tagging not Implemented';
+  SUnConstructedEncoding =
+    'Sequences Must Use Constructed Encoding (see X.690 8.9.1/8.10.1)';
+  SUnConstructedEncoding2 =
+    'Sets Must Use Constructed Encoding (see X.690 8.11.1/8.12.1)';
+  SMalformedObject = 'Malformed Object %s';
+  SUnSupportedTag = 'Unsupported Tag Number';
+  SConvertError = 'EIOCryptoLibException Converting Stream to Byte Array: %s';
+  SEncodingError = 'Encoding Error in GetInstance:  %s  "obj"';
+  SDataNil = '"data"';
+  SInvalidRange = 'Must be in the Range 0 to 7", "padBits"';
+  SPadBitError = 'If "data" is Empty, "padBits" Must be 0';
+  SUnalignedData = 'Attempt to Get non-octet Aligned Data from BIT STRING"';
+  STruncatedBitString = 'Truncated BIT STRING Detected", "octets"';
+  SNotImplemented = 'Not Implemented %s';
+  SUnConstructedTag = 'Explicit Tags Must be Constructed (see X.690 8.14.2)';
+  SParsingError = '%s';
+  SEmptyInput = 'Input Cannot be Empty "astr"';
+  SInvalidValue = 'Byte Value Should Have 1 Byte in it'', "val"';
+  SInvalidBooleanValue = 'BOOLEAN Value Should Have 1 Byte in it", "Value"';
+  SMalformedEnumerated = 'Malformed Enumerated';
+  SZeroLength = 'Enumerated has Zero Length, "enc"';
+  SInvalidEncoding = 'Invalid Encoding Value: %d';
+  SFewObject = 'Too Few Objects in Input Vector, "v"';
+  SVectorTooLarge = 'Input Vector too Large", "vector"';
+  SNoTaggedObjectFound =
+    'No Tagged Object Found in Vector. Structure Doesn ''t Seem to be of Type External, "Vector"';
+  SInvalidEncodingValue = 'Invalid Encoding Value';
+  SObjectNil = ' "obj" Can''t be Nil';
+  SValueNil = ' "value" Can''t be Nil';
+  SMalformedInteger = 'Malformed Integer';
+  SIdentifierNil = 'Identifier Cannot be Empty';
+  SInvalidOID = '"String " %s is " not an OID"';
+  SInvalidBranchId = '"String " %s " not a valid OID branch", "branchID"';
+  SIllegalCharacters = 'String Contains Illegal Characters "str"';
+
+  // ** Start Stream Operations ** //
+
+type
+  TStreamHelper = class helper for TStream
+
+  public
+
+    function ReadByte(): Int32;
+    procedure WriteByte(b: Byte); inline;
+  end;
+
+type
+  TStreamSorter = class sealed(TObject)
+
+  public
+
+    class function Read(input: TStream; var buffer: TCryptoLibByteArray;
+      offset, count: Int32): Int32; static;
+    class function ReadByte(input: TStream): Int32; static;
+  end;
+
+type
+  TStreamUtils = class sealed(TObject)
+
+  strict private
+  const
+    BufferSize = Int32(512);
+
+  public
+
+    class procedure Drain(const inStr: TStream); static;
+    class function ReadAll(const inStr: TStream): TCryptoLibByteArray;
+      static; inline;
+    class function ReadAllLimited(const inStr: TStream; limit: Int32)
+      : TCryptoLibByteArray; static; inline;
+    class function ReadFully(const inStr: TStream; var buf: TCryptoLibByteArray)
+      : Int32; overload; static; inline;
+    class function ReadFully(const inStr: TStream; var buf: TCryptoLibByteArray;
+      off, len: Int32): Int32; overload; static;
+    class procedure PipeAll(const inStr, outStr: TStream); static;
+    /// <summary>
+    /// Pipe all bytes from <c>inStr</c> to <c>outStr</c>, throwing <c>
+    /// EStreamOverflowCryptoLibException</c> if greater than <c>limit</c> bytes in <c>
+    /// inStr</c>.
+    /// </summary>
+    /// <param name="inStr">
+    /// Input Stream
+    /// </param>
+    /// <param name="limit">
+    /// Limit
+    /// </param>
+    /// <param name="outStr">
+    /// Output Stream
+    /// </param>
+    /// <returns>
+    /// The number of bytes actually transferred, if not greater than <c>
+    /// limit</c>
+    /// </returns>
+    /// <exception cref="EStreamOverflowCryptoLibException" />
+    class function PipeAllLimited(const inStr: TStream; limit: Int64;
+      const outStr: TStream): Int64; static;
+
+    class procedure WriteBufTo(const buf: TMemoryStream; const output: TStream);
+      overload; static; inline;
+
+    class function WriteBufTo(const buf: TMemoryStream;
+      const output: TCryptoLibByteArray; offset: Int32): Int32; overload;
+      static; inline;
+
+    class procedure WriteZeroes(const outStr: TStream; count: Int64); static;
+
+  end;
+
+type
+  TBaseInputStream = class abstract(TStream)
+
+{$IFDEF DELPHI}
+  private
+
+    function GetPosition: Int64; inline;
+    procedure SetPosition(const Pos: Int64); inline;
+    procedure SetSize64(const NewSize: Int64); inline;
+{$ENDIF DELPHI}
+  protected
+
+{$IFDEF FPC}
+    function GetPosition: Int64; override;
+    procedure SetPosition(const Pos: Int64); override;
+    procedure SetSize64(const NewSize: Int64); override;
+{$ENDIF FPC}
+    function GetSize: Int64; override;
+    procedure SetSize(NewSize: LongInt); overload; override;
+    procedure SetSize(const NewSize: Int64); overload; override;
+
+  public
+    function ReadByte: Int32; virtual;
+
+    function Read(var buffer; count: LongInt): LongInt; overload; override;
+    function Write(const buffer; count: LongInt): LongInt; overload; override;
+
+    function Read(buffer: TCryptoLibByteArray; offset, count: LongInt)
+      : LongInt; overload;
+{$IFDEF SUPPORT_TSTREAM_READ_BYTEARRAY_OVERLOAD} override {$ELSE} virtual
+{$ENDIF SUPPORT_TSTREAM_READ_BYTEARRAY_OVERLOAD};
+
+    function Write(const buffer: TCryptoLibByteArray; offset, count: LongInt)
+      : LongInt; overload; {$IFDEF SUPPORT_TSTREAM_WRITE_BYTEARRAY_OVERLOAD} override {$ELSE} virtual
+{$ENDIF SUPPORT_TSTREAM_WRITE_BYTEARRAY_OVERLOAD};
+
+    function Seek(offset: LongInt; Origin: Word): LongInt; overload; override;
+    function Seek(const offset: Int64; Origin: TSeekOrigin): Int64;
+      overload; override;
+
+{$IFNDEF _FIXINSIGHT_}
+    property Size: Int64 read GetSize write SetSize64;
+{$ENDIF}
+    property Position: Int64 read GetPosition write SetPosition;
+  end;
+
+type
+  TFilterStream = class(TStream)
+
+  protected
+  var
+    Fs: TStream;
+
+    function GetPosition: Int64; {$IFDEF FPC} override; {$ENDIF FPC}
+    procedure SetPosition(const Value: Int64); {$IFDEF FPC} override;
+{$ENDIF FPC}
+    function GetSize: Int64; override;
+
+  public
+    constructor Create(const s: TStream);
+
+    property Size: Int64 read GetSize;
+    property Position: Int64 read GetPosition write SetPosition;
+
+    function Seek(const offset: Int64; Origin: TSeekOrigin): Int64; override;
+    function Read(var buffer; count: LongInt): LongInt; override;
+    function Write(const buffer; count: LongInt): LongInt; override;
+
+    function ReadByte(): Int32;
+    procedure WriteByte(Value: Byte);
+
+  end;
+
+type
+  TLimitedInputStream = class abstract(TBaseInputStream)
+
+  strict private
+  var
+    F_limit: Int32;
+  strict protected
+  var
+    F_in: TStream;
+
+    procedure SetParentEofDetect(&on: Boolean);
+
+  public
+    constructor Create(inStream: TStream; limit: Int32);
+    function GetRemaining(): Int32; virtual;
+
+  end;
+
+type
+  TDefiniteLengthInputStream = class(TLimitedInputStream)
+
+  strict private
+
+  var
+    F_originalLength, F_remaining: Int32;
+
+    function GetRemaining: Int32; reintroduce; inline;
+    class function GetEmptyBytes: TCryptoLibByteArray; static; inline;
+
+  public
+
+    constructor Create(inStream: TStream; length: Int32);
+
+    function ReadByte(): Int32; override;
+
+    function Read(buf: TCryptoLibByteArray; off, len: LongInt)
+      : LongInt; override;
+
+    procedure ReadAllIntoByteArray(var buf: TCryptoLibByteArray);
+
+    function ToArray: TCryptoLibByteArray;
+
+    property Remaining: Int32 read GetRemaining;
+    class property EmptyBytes: TCryptoLibByteArray read GetEmptyBytes;
+
+  end;
+
+type
+
+  /// <summary>
+  /// a general purpose ASN.1 decoder - note: this class differs from the <br />
+  /// others in that it returns null after it has read the last object in <br />
+  /// the stream. If an ASN.1 Null is encountered a DerBER Null object is <br />
+  /// returned. <br />
+  /// </summary>
+  TAsn1InputStream = class(TFilterStream)
+
+  strict private
+
+  var
+    Flimit: Int32;
+    FtmpBuffers: TCryptoLibMatrixByteArray;
+    FStream: TStream;
+
+    /// <summary>
+    /// build an object given its tag and the number of bytes to construct it
+    /// from.
+    /// </summary>
+    function BuildObject(tag, tagNo, length: Int32): IAsn1Object;
+
+  public
+
+    constructor Create(const inputStream: TStream); overload;
+
+    /// <summary>
+    /// Create an ASN1InputStream where no DER object will be longer than
+    /// limit.
+    /// </summary>
+    /// <param name="inputStream">
+    /// stream containing ASN.1 encoded data.
+    /// </param>
+    /// <param name="limit">
+    /// maximum size of a DER encoded object.
+    /// </param>
+    constructor Create(const inputStream: TStream; limit: Int32); overload;
+
+    destructor Destroy(); override;
+
+    /// <summary>
+    /// the stream is automatically limited to the length of the input array.
+    /// </summary>
+    /// <param name="input">
+    /// array containing ASN.1 encoded data.
+    /// </param>
+    constructor Create(const input: TCryptoLibByteArray); overload;
+
+    function ReadObject(): IAsn1Object;
+
+    function BuildEncodableVector(): IAsn1EncodableVector;
+
+    function BuildDerEncodableVector(const dIn: TDefiniteLengthInputStream)
+      : IAsn1EncodableVector; virtual;
+
+    function CreateDerSequence(const dIn: TDefiniteLengthInputStream)
+      : IDerSequence; virtual;
+
+    function CreateDerSet(const dIn: TDefiniteLengthInputStream)
+      : IDerSet; virtual;
+
+    class function FindLimit(const input: TStream): Int32; static;
+
+    class function ReadTagNumber(const s: TStream; tag: Int32): Int32; static;
+
+    class function ReadLength(const s: TStream; limit: Int32): Int32; static;
+
+    class function GetBuffer(const defIn: TDefiniteLengthInputStream;
+      const tmpBuffers: TCryptoLibMatrixByteArray): TCryptoLibByteArray;
+      static; inline;
+
+    class function CreatePrimitiveDerObject(tagNo: Int32;
+      const defIn: TDefiniteLengthInputStream;
+      const tmpBuffers: TCryptoLibMatrixByteArray): IAsn1Object; static;
+  end;
+
+type
+  TDerOutputStream = class(TFilterStream)
+
+  strict private
+    procedure WriteLength(length: Int32);
+
+  strict protected
+    procedure WriteNull();
+
+  public
+    constructor Create(const os: TStream);
+    procedure WriteEncoded(tag: Int32;
+      const bytes: TCryptoLibByteArray); overload;
+    procedure WriteEncoded(tag: Int32; first: Byte;
+      const bytes: TCryptoLibByteArray); overload;
+    procedure WriteEncoded(tag: Int32; const bytes: TCryptoLibByteArray;
+      offset, length: Int32); overload;
+    procedure WriteEncoded(flags, tagNo: Int32;
+      const bytes: TCryptoLibByteArray); overload;
+    procedure WriteTag(flags, tagNo: Int32);
+
+    procedure WriteObject(const obj: IAsn1Encodable); overload; virtual;
+    procedure WriteObject(const obj: IAsn1Object); overload; virtual;
+
+  end;
+
+type
+  TAsn1OutputStream = class sealed(TDerOutputStream)
+
+  public
+    constructor Create(os: TStream);
+
+  end;
+
+type
+  // TODO Make Obsolete in favour of Asn1OutputStream?
+  TBerOutputStream = class sealed(TDerOutputStream)
+
+  public
+
+    constructor Create(os: TStream);
+
+  end;
+
+type
+  TConstructedOctetStream = class(TBaseInputStream)
+
+  strict private
+  var
+    F_parser: IAsn1StreamParser;
+    F_first: Boolean;
+    F_currentStream: TStream;
+
+  public
+    constructor Create(const parser: IAsn1StreamParser);
+    function Read(buffer: TCryptoLibByteArray; offset, count: LongInt)
+      : LongInt; override;
+    function ReadByte(): Int32; override;
+  end;
+
+type
+  TIndefiniteLengthInputStream = class(TLimitedInputStream)
+
+  strict private
+  var
+    F_lookAhead: Int32;
+    F_eofOn00: Boolean;
+
+    function CheckForEof(): Boolean; inline;
+    function RequireByte(): Int32; inline;
+
+  public
+    constructor Create(inStream: TStream; limit: Int32);
+    procedure SetEofOn00(eofOn00: Boolean);
+
+    function Read(buffer: TCryptoLibByteArray; offset, count: LongInt)
+      : LongInt; override;
+
+    function ReadByte(): Int32; override;
+
+  end;
+
+  // ** End Stream Operations ** //
+
+type
+  TCollectionUtilities = class sealed(TObject)
+
+  public
+
+    class function ToStructuredString(c: TList<IAsn1Encodable>): String; static;
+
+  end;
+
+type
+
+  TAsn1Encodable = class abstract(TInterfacedObject, IAsn1Encodable,
+    IAsn1Convertible)
+
+  public
+
+    const
+    Der: String = 'DER';
+    Ber: String = 'BER';
+
+    function GetEncoded(): TCryptoLibByteArray; overload;
+    function GetEncoded(const encoding: String): TCryptoLibByteArray; overload;
+
+    /// <summary>
+    /// Return the DER encoding of the object, null if the DER encoding can
+    /// not be made.
+    /// </summary>
+    /// <returns>
+    /// return a DER byte array, null otherwise.
+    /// </returns>
+    function GetDerEncoded(): TCryptoLibByteArray;
+
+    function Equals(const other: IAsn1Convertible): Boolean; reintroduce;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    function ToAsn1Object(): IAsn1Object; virtual; abstract;
+
+  end;
+
+type
+
+  TAsn1Object = class abstract(TAsn1Encodable, IAsn1Object)
+
+  strict protected
+
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+      virtual; abstract;
+
+    function Asn1GetHashCode(): Int32; virtual; abstract;
+
+  public
+    /// <summary>Create a base ASN.1 object from a byte array.</summary>
+    /// <param name="data">The byte array to parse.</param>
+    /// <returns>The base ASN.1 object represented by the byte array.</returns>
+    /// <exception cref="IOException">
+    /// If there is a problem parsing the data, or parsing an object did not exhaust the available data.
+    /// </exception>
+    class function FromByteArray(const data: TCryptoLibByteArray)
+      : IAsn1Object; static;
+
+    /// <summary>Read a base ASN.1 object from a stream.</summary>
+    /// <param name="inStr">The stream to parse.</param>
+    /// <returns>The base ASN.1 object represented by the byte array.</returns>
+    /// <exception cref="IOException">If there is a problem parsing the data.</exception>
+    class function FromStream(const inStr: TStream): IAsn1Object; static;
+
+    function ToAsn1Object(): IAsn1Object; override;
+
+    procedure Encode(const derOut: TStream); virtual; abstract;
+
+    function CallAsn1Equals(const obj: IAsn1Object): Boolean;
+
+    function CallAsn1GetHashCode(): Int32;
+
+  end;
+
+type
+  TDerObjectIdentifier = class(TAsn1Object, IDerObjectIdentifier)
+
+  strict private
+
+  const
+    LONG_LIMIT = Int64((Int64($7FFFFFFFFFFFFFFF) shr 7) - $7F);
+
+  class var
+
+    FLock: TCriticalSection;
+    Fcache: array [0 .. 1023] of IDerObjectIdentifier;
+
+  var
+    Fidentifier: String;
+    Fbody: TCryptoLibByteArray;
+
+    class procedure Boot(); static;
+    class constructor CreateDerObjectIdentifier();
+    class destructor DestroyDerObjectIdentifier();
+
+    constructor Create(const oid: IDerObjectIdentifier;
+      const branchID: String); overload;
+    constructor Create(const bytes: TCryptoLibByteArray); overload;
+
+    function GetID: String; inline;
+
+    procedure WriteField(const outputStream: TStream;
+      fieldValue: Int64); overload;
+    procedure WriteField(const outputStream: TStream;
+      const fieldValue: TBigInteger); overload;
+    procedure DoOutput(const bOut: TMemoryStream); overload;
+    function GetBody(): TCryptoLibByteArray;
+    class function IsValidBranchID(const branchID: String; start: Int32)
+      : Boolean; static;
+    class function IsValidIdentifier(const identifier: String): Boolean; static;
+    class function MakeOidStringFromBytes(const bytes: TCryptoLibByteArray)
+      : String; static;
+
+  strict protected
+    function Asn1GetHashCode(): Int32; override;
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+
+  public
+    // /**
+    // * return an Oid from the passed in object
+    // *
+    // * @exception ArgumentException if the object cannot be converted.
+    // */
+    class function GetInstance(const obj: TObject): IDerObjectIdentifier;
+      overload; static;
+
+    // /**
+    // * return an Oid from the passed in byte array
+    // */
+    class function GetInstance(const obj: TCryptoLibByteArray)
+      : IDerObjectIdentifier; overload; static; inline;
+
+    // /**
+    // * return an object Identifier from a tagged object.
+    // *
+    // * @param obj the tagged object holding the object we want
+    // * @param explicitly true if the object is meant to be explicitly
+    // *              tagged false otherwise.
+    // * @exception ArgumentException if the tagged object cannot
+    // *               be converted.
+    // */
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      explicitly: Boolean): IDerObjectIdentifier; overload; static; inline;
+
+    class function FromOctetString(const enc: TCryptoLibByteArray)
+      : IDerObjectIdentifier; static;
+
+    constructor Create(const identifier: String); overload;
+
+    property ID: String read GetID;
+
+    function Branch(const branchID: String): IDerObjectIdentifier; virtual;
+
+    // /**
+    // * Return  true if this oid is an extension of the passed in branch, stem.
+    // * @param stem the arc or branch that is a possible parent.
+    // * @return  true if the branch is on the passed in stem, false otherwise.
+    // */
+
+    function &on(const stem: IDerObjectIdentifier): Boolean; virtual;
+
+    procedure Encode(const derOut: TStream); override;
+
+    function ToString(): String; override;
+
+  end;
+
+type
+  TAsn1EncodableVector = class(TInterfacedObject, IAsn1EncodableVector)
+
+  strict private
+  var
+
+    Flist: TList<IAsn1Encodable>;
+
+    function GetCount: Int32;
+    function GetSelf(Index: Int32): IAsn1Encodable;
+
+  public
+    class function FromEnumerable(const e: TList<IAsn1Encodable>)
+      : IAsn1EncodableVector; static;
+
+    constructor Create(); overload;
+    constructor Create(const v: array of IAsn1Encodable); overload;
+
+    destructor Destroy(); override;
+
+    procedure Add(const objs: array of IAsn1Encodable);
+
+    procedure AddOptional(const objs: array of IAsn1Encodable);
+
+    property Self[Index: Int32]: IAsn1Encodable read GetSelf; default;
+
+    property count: Int32 read GetCount;
+
+    function GetEnumerable: TCryptoLibGenericArray<IAsn1Encodable>; virtual;
+
+  end;
+
+type
+  TAsn1Generator = class abstract(TInterfacedObject, IAsn1Generator)
+
+  strict private
+  var
+    F_out: TStream;
+
+  strict protected
+    constructor Create(outStream: TStream);
+    function GetOut: TStream; inline;
+    property &Out: TStream read GetOut;
+
+  public
+    procedure AddObject(const obj: IAsn1Encodable); virtual; abstract;
+
+    function GetRawOutputStream(): TStream; virtual; abstract;
+
+    procedure Close(); virtual; abstract;
+  end;
+
+type
+  /// <summary>
+  /// A Null object.
+  /// </summary>
+  TAsn1Null = class abstract(TAsn1Object, IAsn1Null)
+
+  public
+
+    function ToString(): String; override;
+
+  end;
+
+type
+  TAsn1OctetString = class abstract(TAsn1Object, IAsn1OctetString,
+    IAsn1OctetStringParser)
+
+  strict private
+  var
+    FStr: TCryptoLibByteArray;
+
+  strict protected
+    function GetStr: TCryptoLibByteArray; inline;
+    function GetParser: IAsn1OctetStringParser; inline;
+    function Asn1GetHashCode(): Int32; override;
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+
+  public
+    property Str: TCryptoLibByteArray read GetStr;
+    property parser: IAsn1OctetStringParser read GetParser;
+
+    /// <summary>
+    /// return an Octet string from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want.
+    /// </param>
+    /// <param name="isExplicit">
+    /// explicitly true if the object is meant to be explicitly tagged false
+    /// otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IAsn1OctetString; overload; static;
+    /// <summary>
+    /// return an Octet string from the given object.
+    /// </summary>
+    /// <param name="obj">
+    /// the object we want converted.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IAsn1OctetString;
+      overload; static;
+
+    /// <param name="Str">
+    /// the octets making up the octet string.
+    /// </param>
+    constructor Create(const Str: TCryptoLibByteArray); overload;
+
+    constructor Create(const obj: IAsn1Encodable); overload;
+
+    function GetOctetStream(): TStream;
+
+    function GetOctets(): TCryptoLibByteArray; virtual;
+
+    function ToString(): String; override;
+
+  end;
+
+type
+  /// <summary>
+  /// return an Asn1Sequence from the given object.
+  /// </summary>
+  TAsn1Sequence = class abstract(TAsn1Object, IAsn1Sequence)
+
+  strict private
+  var
+    FSeq: TList<IAsn1Encodable>;
+
+  type
+    TAsn1SequenceParserImpl = class sealed(TInterfacedObject,
+      IAsn1SequenceParserImpl, IAsn1SequenceParser)
+
+    strict private
+    var
+      Fouter: IAsn1Sequence;
+      Fmax, Findex: Int32;
+
+    public
+      constructor Create(const outer: IAsn1Sequence);
+      function ReadObject(): IAsn1Convertible;
+      function ToAsn1Object(): IAsn1Object;
+
+    end;
+
+  strict protected
+    function GetCount: Int32; virtual;
+    function GetParser: IAsn1SequenceParser; virtual;
+    function GetSelf(Index: Integer): IAsn1Encodable; virtual;
+    function GetCurrent(const e: IAsn1Encodable): IAsn1Encodable;
+    function Asn1GetHashCode(): Int32; override;
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+    procedure AddObject(const obj: IAsn1Encodable); inline;
+
+    constructor Create(capacity: Int32);
+
+  public
+
+    destructor Destroy(); override;
+
+    function ToString(): String; override;
+
+    function GetEnumerable: TCryptoLibGenericArray<IAsn1Encodable>; virtual;
+
+    // /**
+    // * return the object at the sequence position indicated by index.
+    // *
+    // * @param index the sequence number (starting at zero) of the object
+    // * @return the object at the sequence position indicated by index.
+    // */
+    property Self[Index: Int32]: IAsn1Encodable read GetSelf; default;
+
+    /// <summary>
+    /// return an Asn1Sequence from the given object.
+    /// </summary>
+    /// <param name="obj">
+    /// the object we want converted.
+    /// </param>
+    /// <exception cref="EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IAsn1Sequence;
+      overload; static;
+
+    /// <summary>
+    /// return an Asn1Sequence from the given object.
+    /// </summary>
+    /// <param name="obj">
+    /// the byte array we want converted.
+    /// </param>
+    /// <exception cref="EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TCryptoLibByteArray): IAsn1Sequence;
+      overload; static;
+
+    // /**
+    // * Return an ASN1 sequence from a tagged object. There is a special
+    // * case here, if an object appears to have been explicitly tagged on
+    // * reading but we were expecting it to be implicitly tagged in the
+    // * normal course of events it indicates that we lost the surrounding
+    // * sequence - so we need to add it back (this will happen if the tagged
+    // * object is a sequence that contains other sequences). If you are
+    // * dealing with implicitly tagged sequences you really <b>should</b>
+    // * be using this method.
+    // *
+    // * @param obj the tagged object.
+    // * @param explicitly true if the object is meant to be explicitly tagged,
+    // *          false otherwise.
+    // * @exception ArgumentException if the tagged object cannot
+    // *          be converted.
+    // */
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      explicitly: Boolean): IAsn1Sequence; overload; static;
+
+    property parser: IAsn1SequenceParser read GetParser;
+    property count: Int32 read GetCount;
+
+  end;
+
+type
+  TDerOctetString = class(TAsn1OctetString, IDerOctetString)
+
+  public
+    /// <param name="str">The octets making up the octet string.</param>
+    constructor Create(const Str: TCryptoLibByteArray); overload;
+    constructor Create(const obj: IAsn1Encodable); overload;
+
+    destructor Destroy(); override;
+
+    procedure Encode(const derOut: TStream); overload; override;
+    class procedure Encode(const derOut: TDerOutputStream;
+      const bytes: TCryptoLibByteArray; offset, length: Int32); reintroduce;
+      overload; static; inline;
+
+  end;
+
+type
+  TBerOctetString = class(TDerOctetString, IBerOctetString)
+
+  strict private
+  const
+    MaxLength = Int32(1000);
+
+  var
+    Focts: TList<IDerOctetString>;
+
+    function GenerateOcts(): TList<IDerOctetString>;
+
+    class function ToBytes(octs: TList<IDerOctetString>)
+      : TCryptoLibByteArray; static;
+
+  public
+
+    /// <inheritdoc />
+    /// <param name="str">The octets making up the octet string.</param>
+    constructor Create(const Str: TCryptoLibByteArray); overload;
+    constructor Create(const octets: TList<IDerOctetString>); overload;
+    constructor Create(const obj: IAsn1Object); overload;
+    constructor Create(const obj: IAsn1Encodable); overload;
+
+    destructor Destroy(); override;
+
+    function GetOctets(): TCryptoLibByteArray; override;
+
+    /// <summary>
+    /// return the DER octets that make up this string.
+    /// </summary>
+    function GetEnumerable: TCryptoLibGenericArray<IDerOctetString>; virtual;
+
+    procedure Encode(const derOut: TStream); override;
+
+    class function FromSequence(const seq: IAsn1Sequence)
+      : IBerOctetString; static;
+
+  end;
+
+type
+
+  /// <summary>
+  /// A Null object.
+  /// </summary>
+  TDerNull = class(TAsn1Null, IDerNull)
+
+  strict private
+
+    class function GetInstance: IDerNull; static; inline;
+
+  const
+    ZeroBytes: TCryptoLibByteArray = Nil;
+
+  strict protected
+    constructor Create(dummy: Int32);
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+    function Asn1GetHashCode(): Int32; override;
+
+  public
+
+    procedure Encode(const derOut: TStream); override;
+    class property Instance: IDerNull read GetInstance;
+
+  end;
+
+type
+  TDerSequence = class(TAsn1Sequence, IDerSequence)
+
+  strict private
+
+    class function GetEmpty: IDerSequence; static; inline;
+
+  public
+
+    class function FromVector(const v: IAsn1EncodableVector)
+      : IDerSequence; static;
+
+    /// <summary>
+    /// create an empty sequence
+    /// </summary>
+    constructor Create(); overload;
+
+    /// <summary>
+    /// create a sequence containing one object
+    /// </summary>
+    constructor Create(const obj: IAsn1Encodable); overload;
+
+    constructor Create(const v: array of IAsn1Encodable); overload;
+
+    /// <summary>
+    /// create a sequence containing a vector of objects.
+    /// </summary>
+    constructor Create(const v: IAsn1EncodableVector); overload;
+
+    destructor Destroy(); override;
+
+    /// <summary>
+    /// A note on the implementation: <br />As Der requires the constructed,
+    /// definite-length model to <br />be used for structured types, this
+    /// varies slightly from the <br />ASN.1 descriptions given. Rather than
+    /// just outputing Sequence, <br />we also have to specify Constructed,
+    /// and the objects length. <br />
+    /// </summary>
+    procedure Encode(const derOut: TStream); override;
+
+    class property Empty: IDerSequence read GetEmpty;
+
+  end;
+
+type
+  TBerSequence = class(TDerSequence, IBerSequence)
+
+  strict private
+
+    class function GetEmpty: IBerSequence; static; inline;
+
+  public
+
+    class function FromVector(const v: IAsn1EncodableVector)
+      : IBerSequence; static;
+
+    /// <summary>
+    /// create an empty sequence
+    /// </summary>
+    constructor Create(); overload;
+
+    /// <summary>
+    /// create a sequence containing one object
+    /// </summary>
+    constructor Create(const obj: IAsn1Encodable); overload;
+
+    constructor Create(const v: array of IAsn1Encodable); overload;
+
+    /// <summary>
+    /// create a sequence containing a vector of objects.
+    /// </summary>
+    constructor Create(const v: IAsn1EncodableVector); overload;
+
+    destructor Destroy(); override;
+
+    /// <summary>
+    /// A note on the implementation: <br />As Der requires the constructed,
+    /// definite-length model to <br />be used for structured types, this
+    /// varies slightly from the <br />ASN.1 descriptions given. Rather than
+    /// just outputing Sequence, <br />we also have to specify Constructed,
+    /// and the objects length. <br />
+    /// </summary>
+    procedure Encode(const derOut: TStream); override;
+
+    class property Empty: IBerSequence read GetEmpty;
+
+  end;
+
+type
+  /// **
+  // * ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by
+  // * a [n] where n is some number - these are assumed to follow the construction
+  // * rules (as with sequences).
+  // */
+  TAsn1TaggedObject = class abstract(TAsn1Object, IAsn1TaggedObject,
+    IAsn1TaggedObjectParser)
+
+  strict private
+    FtagNo: Int32;
+    Fexplicitly: Boolean;
+    Fobj: IAsn1Encodable;
+
+  strict protected
+    // /**
+    // * @param tagNo the tag number for this object.
+    // * @param obj the tagged object.
+    // */
+    constructor Create(tagNo: Int32; const obj: IAsn1Encodable); overload;
+    // /**
+    // * @param explicitly true if the object is explicitly tagged.
+    // * @param tagNo the tag number for this object.
+    // * @param obj the tagged object.
+    // */
+    constructor Create(explicitly: Boolean; tagNo: Int32;
+      const obj: IAsn1Encodable); overload;
+
+    function GetTagNo: Int32; inline;
+    function Getexplicitly: Boolean; inline;
+    function Getobj: IAsn1Encodable; inline;
+
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+
+    function Asn1GetHashCode(): Int32; override;
+
+  public
+    class function IsConstructed(isExplicit: Boolean; const obj: IAsn1Object)
+      : Boolean; static;
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      explicitly: Boolean): IAsn1TaggedObject; overload; static; inline;
+    class function GetInstance(obj: TObject): IAsn1TaggedObject; overload;
+      static; inline;
+
+    property tagNo: Int32 read GetTagNo;
+    property explicitly: Boolean read Getexplicitly;
+    property obj: IAsn1Encodable read Getobj;
+
+    // /**
+    // * return whether or not the object may be explicitly tagged.
+    // * <p>
+    // * Note: if the object has been read from an input stream, the only
+    // * time you can be sure if isExplicit is returning the true state of
+    // * affairs is if it returns false. An implicitly tagged object may appear
+    // * to be explicitly tagged, so you need to understand the context under
+    // * which the reading was done as well, see GetObject below.</p>
+    // */
+
+    function isExplicit(): Boolean; inline;
+
+    function IsEmpty(): Boolean; inline;
+    // /**
+    // * return whatever was following the tag.
+    // * <p>
+    // * Note: tagged objects are generally context dependent if you're
+    // * trying to extract a tagged object you should be going via the
+    // * appropriate GetInstance method.</p>
+    // */
+    function GetObject(): IAsn1Object; inline;
+    // /**
+    // * Return the object held in this tagged object as a parser assuming it has
+    // * the type of the passed in tag. If the object doesn't have a parser
+    // * associated with it, the base object is returned.
+    // */
+    function GetObjectParser(tag: Int32; isExplicit: Boolean): IAsn1Convertible;
+
+    function ToString(): String; override;
+
+  end;
+
+type
+  TAsn1Tags = class sealed(TObject)
+
+  public
+
+    const
+    &Boolean = Int32($01);
+    &Integer = Int32($02);
+    BitString = Int32($03);
+    OctetString = Int32($04);
+    Null = Int32($05);
+    ObjectIdentifier = Int32($06);
+    &External = Int32($08);
+    Enumerated = Int32($0A);
+    Sequence = Int32($10);
+    SequenceOf = Int32($10); // for completeness
+    &Set = Int32($11);
+    SetOf = Int32($11); // for completeness
+
+    NumericString = Int32($12);
+    PrintableString = Int32($13);
+    T61String = Int32($14);
+    VideotexString = Int32($15);
+    IA5String = Int32($16);
+    UtcTime = Int32($17);
+    GeneralizedTime = Int32($18);
+    GraphicString = Int32($19);
+    VisibleString = Int32($1A);
+    GeneralString = Int32($1B);
+    UniversalString = Int32($1C);
+    BmpString = Int32($1E);
+    Utf8String = Int32($0C);
+
+    Constructed = Int32($20);
+    Application = Int32($40);
+    Tagged = Int32($80);
+  end;
+
+type
+  /// <summary>
+  /// return an Asn1Set from the given object.
+  /// </summary>
+  TAsn1Set = class abstract(TAsn1Object, IAsn1Set)
+
+  strict private
+  var
+    F_set: TList<IAsn1Encodable>;
+    FisSorted: Boolean;
+
+    /// <summary>
+    /// return true if a &lt;= b (arrays are assumed padded with zeros).
+    /// </summary>
+    function LessThanOrEqual(const a, b: TCryptoLibByteArray): Boolean; inline;
+
+  type
+    TAsn1SetParserImpl = class sealed(TInterfacedObject, IAsn1SetParserImpl,
+      IAsn1SetParser)
+
+    strict private
+      Fouter: IAsn1Set;
+      Fmax, Findex: Int32;
+
+    public
+      constructor Create(const outer: IAsn1Set);
+      function ReadObject(): IAsn1Convertible;
+      function ToAsn1Object(): IAsn1Object;
+
+    end;
+
+  strict protected
+    function GetCount: Int32; virtual;
+    function GetParser: IAsn1SetParser; inline;
+    function GetSelf(Index: Integer): IAsn1Encodable; virtual;
+    function GetCurrent(const e: IAsn1Encodable): IAsn1Encodable;
+    function Asn1GetHashCode(): Int32; override;
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+    procedure AddObject(const obj: IAsn1Encodable); inline;
+    procedure Sort();
+
+    constructor Create(capacity: Int32);
+
+  public
+    destructor Destroy(); override;
+
+  public
+
+    function ToString(): String; override;
+
+    function ToArray(): TCryptoLibGenericArray<IAsn1Encodable>; virtual;
+
+    function GetEnumerable: TCryptoLibGenericArray<IAsn1Encodable>; virtual;
+
+    // /**
+    // * return the object at the sequence position indicated by index.
+    // *
+    // * @param index the sequence number (starting at zero) of the object
+    // * @return the object at the sequence position indicated by index.
+    // */
+    property Self[Index: Int32]: IAsn1Encodable read GetSelf; default;
+
+    /// <summary>
+    /// return an ASN1Set from the given object.
+    /// </summary>
+    /// <param name="obj">
+    /// the object we want converted.
+    /// </param>
+    /// <exception cref="EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IAsn1Set; overload; static;
+
+    /// <summary>
+    /// return an Asn1Set from the given object.
+    /// </summary>
+    /// <param name="obj">
+    /// the byte array we want converted.
+    /// </param>
+    /// <exception cref="EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TCryptoLibByteArray): IAsn1Set;
+      overload; static;
+
+    // /**
+    // * Return an ASN1 sequence from a tagged object. There is a special
+    // * case here, if an object appears to have been explicitly tagged on
+    // * reading but we were expecting it to be implicitly tagged in the
+    // * normal course of events it indicates that we lost the surrounding
+    // * sequence - so we need to add it back (this will happen if the tagged
+    // * object is a sequence that contains other sequences). If you are
+    // * dealing with implicitly tagged sequences you really <b>should</b>
+    // * be using this method.
+    // *
+    // * @param obj the tagged object.
+    // * @param explicitly true if the object is meant to be explicitly tagged,
+    // *          false otherwise.
+    // * @exception ArgumentException if the tagged object cannot
+    // *          be converted.
+    // */
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      explicitly: Boolean): IAsn1Set; overload; static;
+
+    property parser: IAsn1SetParser read GetParser;
+    property count: Int32 read GetCount;
+
+  end;
+
+type
+
+  /// <summary>
+  /// A Der encoded set object
+  /// </summary>
+  TDerSet = class(TAsn1Set, IDerSet)
+
+  strict private
+    class function GetEmpty: IDerSet; static; inline;
+
+  public
+
+    class function FromVector(const v: IAsn1EncodableVector): IDerSet;
+      overload; static;
+    class function FromVector(const v: IAsn1EncodableVector;
+      needsSorting: Boolean): IDerSet; overload; static;
+
+    /// <summary>
+    /// create an empty set
+    /// </summary>
+    constructor Create(); overload;
+
+    /// <param name="obj">
+    /// a single object that makes up the set.
+    /// </param>
+    constructor Create(const obj: IAsn1Encodable); overload;
+
+    constructor Create(const v: array of IAsn1Encodable); overload;
+
+    /// <param name="v">
+    /// a vector of objects making up the set.
+    /// </param>
+    constructor Create(const v: IAsn1EncodableVector); overload;
+
+    constructor Create(const v: IAsn1EncodableVector;
+      needsSorting: Boolean); overload;
+
+    destructor Destroy(); override;
+
+    /// <summary>
+    /// A note on the implementation: <br />As Der requires the constructed,
+    /// definite-length model to <br />be used for structured types, this
+    /// varies slightly from the <br />ASN.1 descriptions given. Rather than
+    /// just outputing Set, <br />we also have to specify Constructed, and
+    /// the objects length. <br />
+    /// </summary>
+    procedure Encode(const derOut: TStream); override;
+
+    class property Empty: IDerSet read GetEmpty;
+
+  end;
+
+type
+  TAsn1StreamParser = class(TInterfacedObject, IAsn1StreamParser)
+
+  strict private
+  var
+    F_in: TStream;
+    F_limit: Int32;
+    FtmpBuffers: TCryptoLibMatrixByteArray;
+
+    procedure Set00Check(enabled: Boolean); inline;
+
+  public
+    constructor Create(const inStream: TStream); overload;
+    constructor Create(const inStream: TStream; limit: Int32); overload;
+    constructor Create(const encoding: TCryptoLibByteArray); overload;
+
+    destructor Destroy; override;
+
+    function ReadIndef(tagValue: Int32): IAsn1Convertible;
+    function ReadImplicit(Constructed: Boolean; tag: Int32): IAsn1Convertible;
+
+    function ReadTaggedObject(Constructed: Boolean; tag: Int32): IAsn1Object;
+
+    function ReadObject(): IAsn1Convertible; virtual;
+
+    function ReadVector(): IAsn1EncodableVector; inline;
+  end;
+
+type
+  TDerSetParser = class(TInterfacedObject, IAsn1SetParser, IAsn1Convertible,
+    IDerSetParser)
+
+  strict private
+  var
+    F_parser: IAsn1StreamParser;
+
+  public
+
+    constructor Create(const parser: IAsn1StreamParser);
+    function ReadObject(): IAsn1Convertible; inline;
+    function ToAsn1Object(): IAsn1Object; inline;
+
+  end;
+
+type
+  TDerSequenceParser = class(TInterfacedObject, IAsn1SequenceParser,
+    IAsn1Convertible, IDerSequenceParser)
+
+  strict private
+  var
+    F_parser: IAsn1StreamParser;
+
+  public
+
+    constructor Create(const parser: IAsn1StreamParser);
+    function ReadObject(): IAsn1Convertible; inline;
+    function ToAsn1Object(): IAsn1Object; inline;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Base class for an application specific object
+  /// </summary>
+  TDerApplicationSpecific = class(TAsn1Object, IDerApplicationSpecific)
+
+  strict private
+  var
+    FisConstructed: Boolean;
+    Ftag: Int32;
+    Foctets: TCryptoLibByteArray;
+
+    class function ReplaceTagNumber(newTag: Int32;
+      const input: TCryptoLibByteArray): TCryptoLibByteArray; static;
+
+  strict protected
+    function GetApplicationTag: Int32; inline;
+    function GetLengthOfHeader(const data: TCryptoLibByteArray): Int32; inline;
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+    function Asn1GetHashCode(): Int32; override;
+
+  public
+    constructor Create(IsConstructed: Boolean; tag: Int32;
+      const octets: TCryptoLibByteArray); overload;
+    constructor Create(tag: Int32; const octets: TCryptoLibByteArray); overload;
+    constructor Create(tag: Int32; const obj: IAsn1Encodable); overload;
+    constructor Create(isExplicit: Boolean; tag: Int32;
+      const obj: IAsn1Encodable); overload;
+    constructor Create(tagNo: Int32; const vec: IAsn1EncodableVector); overload;
+
+    function IsConstructed(): Boolean; inline;
+    function GetContents(): TCryptoLibByteArray; inline;
+
+    /// <summary>
+    /// Return the enclosed object assuming explicit tagging.
+    /// </summary>
+    /// <returns>
+    /// the resulting object
+    /// </returns>
+    /// <exception cref="ClpCryptoLibTypes|EIOCryptoLibException">
+    /// if reconstruction fails.
+    /// </exception>
+    function GetObject(): IAsn1Object; overload; inline;
+
+    /// <summary>
+    /// Return the enclosed object assuming implicit tagging.
+    /// </summary>
+    /// <param name="derTagNo">
+    /// the type tag that should be applied to the object's contents.
+    /// </param>
+    /// <returns>
+    /// the resulting object
+    /// </returns>
+    /// <exception cref="ClpCryptoLibTypes|EIOCryptoLibException">
+    /// if reconstruction fails.
+    /// </exception>
+    function GetObject(derTagNo: Int32): IAsn1Object; overload; inline;
+
+    procedure Encode(const derOut: TStream); override;
+
+    property ApplicationTag: Int32 read GetApplicationTag;
+  end;
+
+type
+  TBerApplicationSpecific = class(TDerApplicationSpecific,
+    IBerApplicationSpecific)
+
+  public
+    constructor Create(tagNo: Int32; const vec: IAsn1EncodableVector);
+
+  end;
+
+type
+  TBerOctetStringParser = class(TInterfacedObject, IAsn1OctetStringParser,
+    IAsn1Convertible, IBerOctetStringParser)
+
+  strict private
+  var
+    F_parser: IAsn1StreamParser;
+
+  public
+
+    constructor Create(const parser: IAsn1StreamParser);
+    function GetOctetStream(): TStream; inline;
+    function ToAsn1Object(): IAsn1Object;
+
+  end;
+
+type
+  TBerApplicationSpecificParser = class(TInterfacedObject,
+    IAsn1ApplicationSpecificParser, IAsn1Convertible,
+    IBerApplicationSpecificParser)
+
+  strict private
+  var
+    F_tag: Int32;
+    F_parser: IAsn1StreamParser;
+
+  public
+
+    constructor Create(tag: Int32; const parser: IAsn1StreamParser);
+    function ReadObject(): IAsn1Convertible; inline;
+    function ToAsn1Object(): IAsn1Object; inline;
+
+  end;
+
+type
+  TDerStringBase = class abstract(TAsn1Object, IAsn1String, IDerStringBase)
+
+  strict protected
+    constructor Create();
+    function Asn1GetHashCode(): Int32; override;
+  public
+    function GetString(): String; virtual; abstract;
+    function ToString(): String; override;
+  end;
+
+type
+
+  /// <summary>
+  /// Der Bit string object.
+  /// </summary>
+  TDerBitString = class(TDerStringBase, IDerBitString)
+
+  strict private
+  const
+    FTable: array [0 .. 15] of Char = ('0', '1', '2', '3', '4', '5', '6', '7',
+      '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
+
+  strict protected
+  var
+    FmData: TCryptoLibByteArray;
+    FmPadBits: Int32;
+
+    function GetmPadBits: Int32; inline;
+    function GetmData: TCryptoLibByteArray; inline;
+
+    function Asn1GetHashCode(): Int32; override;
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+
+    property mPadBits: Int32 read GetmPadBits;
+    property mData: TCryptoLibByteArray read GetmData;
+  public
+
+    constructor Create(const data: TCryptoLibByteArray;
+      padBits: Int32); overload;
+
+    constructor Create(const data: TCryptoLibByteArray); overload;
+
+    constructor Create(namedBits: Int32); overload;
+
+    constructor Create(const obj: IAsn1Encodable); overload;
+
+    function GetString(): String; override;
+
+    function GetOctets(): TCryptoLibByteArray; virtual;
+
+    function GetBytes(): TCryptoLibByteArray; virtual;
+
+    procedure Encode(const derOut: TStream); override;
+
+    function GetInt32Value: Int32; virtual;
+    property Int32Value: Int32 read GetInt32Value;
+
+    /// <summary>
+    /// return a Der Bit string from the passed in object
+    /// </summary>
+    /// <param name="obj">
+    /// a Bit string or an object that can be converted into one.
+    /// </param>
+    /// <returns>
+    /// return a Der Bit string instance, or null.
+    /// </returns>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IDerBitString; overload;
+      static; inline;
+
+    class function GetInstance(const obj: TCryptoLibByteArray): IDerBitString;
+      overload; static;
+
+    /// <summary>
+    /// return a Der Bit string from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// true if the object is meant to be explicitly tagged false otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerBitString; overload; static; inline;
+
+    class function FromAsn1Octets(const octets: TCryptoLibByteArray)
+      : IDerBitString; static;
+
+  end;
+
+type
+  TBerBitString = class(TDerBitString, IBerBitString)
+
+  public
+    constructor Create(const data: TCryptoLibByteArray;
+      padBits: Int32); overload;
+    constructor Create(const data: TCryptoLibByteArray); overload;
+    constructor Create(namedBits: Int32); overload;
+    constructor Create(const obj: IAsn1Encodable); overload;
+
+    procedure Encode(const derOut: TStream); override;
+
+  end;
+
+type
+  TBerGenerator = class abstract(TAsn1Generator, IBerGenerator)
+
+  strict private
+  var
+    F_tagged, F_isExplicit: Boolean;
+    F_tagNo: Int32;
+
+  strict protected
+    constructor Create(outStream: TStream); overload;
+    constructor Create(outStream: TStream; tagNo: Int32;
+      isExplicit: Boolean); overload;
+
+    procedure WriteHdr(tag: Int32);
+    procedure WriteBerHeader(tag: Int32);
+    procedure WriteBerBody(contentStream: TStream);
+    procedure WriteBerEnd();
+
+  public
+    procedure AddObject(const obj: IAsn1Encodable); override;
+    function GetRawOutputStream(): TStream; override;
+    procedure Close(); override;
+
+  end;
+
+type
+
+  /// <summary>
+  /// A BER Null object.
+  /// </summary>
+  TBerNull = class sealed(TDerNull, IBerNull)
+
+  strict private
+
+    class function GetInstance: IBerNull; static; inline;
+
+    constructor Create(dummy: Int32);
+
+  public
+
+    procedure Encode(const derOut: TStream); override;
+    class property Instance: IBerNull read GetInstance;
+
+  end;
+
+type
+  TBerSequenceGenerator = class(TBerGenerator, IBerSequenceGenerator)
+
+  public
+    constructor Create(outStream: TStream); overload;
+    constructor Create(outStream: TStream; tagNo: Int32;
+      isExplicit: Boolean); overload;
+  end;
+
+type
+  TBerSequenceParser = class(TInterfacedObject, IAsn1SequenceParser,
+    IAsn1Convertible, IBerSequenceParser)
+
+  strict private
+  var
+    F_parser: IAsn1StreamParser;
+
+  public
+
+    constructor Create(const parser: IAsn1StreamParser);
+    function ReadObject(): IAsn1Convertible; inline;
+    function ToAsn1Object(): IAsn1Object; inline;
+
+  end;
+
+type
+
+  /// <summary>
+  /// A Ber encoded set object
+  /// </summary>
+  TBerSet = class sealed(TDerSet, IBerSet)
+
+  strict private
+    class function GetEmpty: IBerSet; static; inline;
+
+  public
+
+    class function FromVector(const v: IAsn1EncodableVector): IBerSet;
+      overload; static;
+    class function FromVector(const v: IAsn1EncodableVector;
+      needsSorting: Boolean): IBerSet; overload; static;
+
+    /// <summary>
+    /// create an empty set
+    /// </summary>
+    constructor Create(); overload;
+
+    /// <param name="obj">
+    /// a single object that makes up the set.
+    /// </param>
+    constructor Create(const obj: IAsn1Encodable); overload;
+
+    /// <param name="v">
+    /// a vector of objects making up the set.
+    /// </param>
+    constructor Create(const v: IAsn1EncodableVector); overload;
+
+    constructor Create(const v: IAsn1EncodableVector;
+      needsSorting: Boolean); overload;
+
+    destructor Destroy(); override;
+
+    /// <summary>
+    /// A note on the implementation: <br />As Ber requires the constructed,
+    /// definite-length model to <br />be used for structured types, this
+    /// varies slightly from the <br />ASN.1 descriptions given. Rather than
+    /// just outputing Set, <br />we also have to specify Constructed, and
+    /// the objects length. <br />
+    /// </summary>
+    procedure Encode(const derOut: TStream); override;
+
+    class property Empty: IBerSet read GetEmpty;
+
+  end;
+
+type
+  TBerSetParser = class(TInterfacedObject, IAsn1SetParser, IAsn1Convertible,
+    IBerSetParser)
+
+  strict private
+  var
+    F_parser: IAsn1StreamParser;
+
+  public
+
+    constructor Create(const parser: IAsn1StreamParser);
+    function ReadObject(): IAsn1Convertible; inline;
+    function ToAsn1Object(): IAsn1Object; inline;
+
+  end;
+
+type
+
+  /// <summary>
+  /// DER TaggedObject - in ASN.1 notation this is any object preceded by <br />
+  /// a [n] where n is some number - these are assumed to follow the
+  /// construction <br />rules (as with sequences). <br />
+  /// </summary>
+  TDerTaggedObject = class(TAsn1TaggedObject, IDerTaggedObject)
+
+  public
+
+    /// <param name="tagNo">
+    /// the tag number for this object.
+    /// </param>
+    /// <param name="obj">
+    /// the tagged object.
+    /// </param>
+    constructor Create(tagNo: Int32; const obj: IAsn1Encodable); overload;
+    /// <param name="explicitly">
+    /// true if an explicitly tagged object.
+    /// </param>
+    /// <param name="tagNo">
+    /// the tag number for this object.
+    /// </param>
+    /// <param name="obj">
+    /// the tagged object.
+    /// </param>
+    constructor Create(explicitly: Boolean; tagNo: Int32;
+      const obj: IAsn1Encodable); overload;
+
+    /// <summary>
+    /// create an implicitly tagged object that contains a zero length
+    /// sequence.
+    /// </summary>
+    /// <param name="tagNo">
+    /// the tag number for this object.
+    /// </param>
+    constructor Create(tagNo: Int32); overload;
+
+    procedure Encode(const derOut: TStream); override;
+
+  end;
+
+type
+
+  /// <summary>
+  /// BER TaggedObject - in ASN.1 notation this is any object preceded by <br />
+  /// a [n] where n is some number - these are assumed to follow the
+  /// construction <br />rules (as with sequences). <br />
+  /// </summary>
+  TBerTaggedObject = class(TDerTaggedObject, IBerTaggedObject)
+
+  public
+
+    /// <param name="tagNo">
+    /// the tag number for this object.
+    /// </param>
+    /// <param name="obj">
+    /// the tagged object.
+    /// </param>
+    constructor Create(tagNo: Int32; const obj: IAsn1Encodable); overload;
+    /// <param name="explicitly">
+    /// true if an explicitly tagged object.
+    /// </param>
+    /// <param name="tagNo">
+    /// the tag number for this object.
+    /// </param>
+    /// <param name="obj">
+    /// the tagged object.
+    /// </param>
+    constructor Create(explicitly: Boolean; tagNo: Int32;
+      const obj: IAsn1Encodable); overload;
+
+    /// <summary>
+    /// create an implicitly tagged object that contains a zero length
+    /// sequence.
+    /// </summary>
+    /// <param name="tagNo">
+    /// the tag number for this object.
+    /// </param>
+    constructor Create(tagNo: Int32); overload;
+
+    procedure Encode(const derOut: TStream); override;
+
+  end;
+
+type
+  TBerTaggedObjectParser = class(TInterfacedObject, IAsn1TaggedObjectParser,
+    IAsn1Convertible, IBerTaggedObjectParser)
+
+  strict private
+  var
+    F_constructed: Boolean;
+    F_tagNumber: Int32;
+    F_parser: IAsn1StreamParser;
+
+    function GetIsConstructed: Boolean; inline;
+    function GetTagNo: Int32; inline;
+
+  public
+    constructor Create(Constructed: Boolean; tagNumber: Int32;
+      const parser: IAsn1StreamParser);
+
+    destructor Destroy; override;
+
+    function GetObjectParser(tag: Int32; isExplicit: Boolean)
+      : IAsn1Convertible; inline;
+
+    function ToAsn1Object(): IAsn1Object;
+
+    property IsConstructed: Boolean read GetIsConstructed;
+    property tagNo: Int32 read GetTagNo;
+
+  end;
+
+type
+  TDerBmpString = class(TDerStringBase, IDerBmpString)
+
+  strict private
+  var
+    FStr: String;
+
+    function GetStr: String; inline;
+
+  strict protected
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+
+  public
+    property Str: String read GetStr;
+
+    /// <summary>
+    /// basic constructor - byte encoded string.
+    /// </summary>
+    constructor Create(const astr: TCryptoLibByteArray); overload;
+
+    /// <summary>
+    /// basic constructor
+    /// </summary>
+    constructor Create(const astr: String); overload;
+
+    function GetString(): String; override;
+
+    procedure Encode(const derOut: TStream); override;
+
+    /// <summary>
+    /// return a BMP string from the given object.
+    /// </summary>
+    /// <param name="obj">
+    /// the object we want converted.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IDerBmpString; overload;
+      static; inline;
+
+    /// <summary>
+    /// return a BMP string from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// true if the object is meant to be explicitly tagged false otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerBmpString; overload; static; inline;
+
+  end;
+
+type
+  TDerBoolean = class(TAsn1Object, IDerBoolean)
+
+  strict private
+  var
+
+    Fvalue: Byte;
+
+    function GetIsTrue: Boolean; inline;
+
+    constructor Create(Value: Boolean); overload;
+
+    class function GetFalse: IDerBoolean; static; inline;
+    class function GetTrue: IDerBoolean; static; inline;
+
+  strict protected
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+    function Asn1GetHashCode(): Int32; override;
+
+  public
+
+    constructor Create(const val: TCryptoLibByteArray); overload;
+
+    procedure Encode(const derOut: TStream); override;
+
+    function ToString(): String; override;
+
+    property IsTrue: Boolean read GetIsTrue;
+
+    class property True: IDerBoolean read GetTrue;
+
+    class property False: IDerBoolean read GetFalse;
+
+    /// <summary>
+    /// return a DerBoolean from the passed in object.
+    /// </summary>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IDerBoolean; overload;
+      static; inline;
+
+    /// <summary>
+    /// return a DerBoolean from the passed in boolean.
+    /// </summary>
+    class function GetInstance(Value: Boolean): IDerBoolean; overload;
+      static; inline;
+
+    /// <summary>
+    /// return a Boolean from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// explicitly true if the object is meant to be explicitly tagged false
+    /// otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerBoolean; overload; static; inline;
+
+    class function FromOctetString(const Value: TCryptoLibByteArray)
+      : IDerBoolean; static;
+
+  end;
+
+type
+  TDerEnumerated = class(TAsn1Object, IDerEnumerated)
+
+  strict private
+
+    class var
+
+      Fcache: array [0 .. 11] of IDerEnumerated;
+
+  var
+    Fbytes: TCryptoLibByteArray;
+
+    function GetValue: TBigInteger; inline;
+    function GetBytes: TCryptoLibByteArray; inline;
+
+  strict protected
+
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+    function Asn1GetHashCode(): Int32; override;
+
+  public
+
+    constructor Create(val: Int32); overload;
+    constructor Create(const val: TBigInteger); overload;
+    constructor Create(const bytes: TCryptoLibByteArray); overload;
+
+    procedure Encode(const derOut: TStream); override;
+
+    property Value: TBigInteger read GetValue;
+    property bytes: TCryptoLibByteArray read GetBytes;
+
+    /// <summary>
+    /// return an integer from the passed in object
+    /// </summary>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+
+    class function GetInstance(const obj: TObject): IDerEnumerated; overload;
+      static; inline;
+
+    /// <summary>
+    /// return an Enumerated from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// true if the object is meant to be explicitly tagged false otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerEnumerated; overload; static; inline;
+
+    class function FromOctetString(const enc: TCryptoLibByteArray)
+      : IDerEnumerated; static;
+
+  end;
+
+type
+  TDerGraphicString = class(TDerStringBase, IDerGraphicString)
+
+  strict private
+  var
+    FmString: TCryptoLibByteArray;
+
+    function GetmString: TCryptoLibByteArray; inline;
+
+  protected
+    function Asn1GetHashCode(): Int32; override;
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+
+  public
+    property mString: TCryptoLibByteArray read GetmString;
+
+    /// <summary>
+    /// basic constructor - with bytes.
+    /// </summary>
+    /// <param name="encoding">
+    /// the byte encoding of the characters making up the string.
+    /// </param>
+    constructor Create(const encoding: TCryptoLibByteArray);
+
+    function GetString(): String; override;
+
+    function GetOctets(): TCryptoLibByteArray; inline;
+
+    procedure Encode(const derOut: TStream); override;
+
+    /// <summary>
+    /// return a Graphic String from the passed in object
+    /// </summary>
+    /// <param name="obj">
+    /// a DerGraphicString or an object that can be converted into one.
+    /// </param>
+    /// <returns>
+    /// return a DerGraphicString instance, or null.
+    /// </returns>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IDerGraphicString; overload;
+      static; inline;
+
+    class function GetInstance(const obj: TCryptoLibByteArray)
+      : IDerGraphicString; overload; static;
+
+    /// <summary>
+    /// return a Graphic string from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// true if the object is meant to be explicitly tagged false otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerGraphicString; overload; static; inline;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Class representing the DER-type External
+  /// </summary>
+  TDerExternal = class(TAsn1Object, IDerExternal)
+
+  strict private
+  var
+    FdirectReference: IDerObjectIdentifier;
+    FindirectReference: IDerInteger;
+    FdataValueDescriptor, FexternalContent: IAsn1Object;
+    Fencoding: Int32;
+
+    function GetDataValueDescriptor: IAsn1Object;
+    function GetDirectReference: IDerObjectIdentifier;
+
+    /// <summary>
+    /// <para>
+    /// The encoding of the content. Valid values are
+    /// </para>
+    /// <para>
+    /// &lt;ul&gt; <br />&lt;li&gt;&lt;code&gt;0&lt;/code&gt;
+    /// single-ASN1-type&lt;/li&gt; <br />
+    /// &lt;li&gt;&lt;code&gt;1&lt;/code&gt; OCTET STRING&lt;/li&gt; <br />
+    /// &lt;li&gt;&lt;code&gt;2&lt;/code&gt; BIT STRING&lt;/li&gt; <br />
+    /// &lt;/ul&gt;
+    /// </para>
+    /// </summary>
+    function GetEncoding: Int32;
+    function GetExternalContent: IAsn1Object;
+    function GetIndirectReference: IDerInteger;
+    procedure SetDataValueDescriptor(const Value: IAsn1Object);
+    procedure SetDirectReference(const Value: IDerObjectIdentifier);
+    procedure SetEncoding(const Value: Int32);
+    procedure SetExternalContent(const Value: IAsn1Object);
+    procedure SetIndirectReference(const Value: IDerInteger);
+
+    class function GetObjFromVector(const v: IAsn1EncodableVector; Index: Int32)
+      : IAsn1Object; static; inline;
+    class procedure WriteEncodable(ms: TMemoryStream; const e: IAsn1Encodable);
+      static; inline;
+
+  strict protected
+    function Asn1GetHashCode(): Int32; override;
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+
+  public
+    constructor Create(const vector: IAsn1EncodableVector); overload;
+
+    /// <summary>
+    /// Creates a new instance of DerExternal <br />See X.690 for more
+    /// informations about the meaning of these parameters
+    /// </summary>
+    /// <param name="directReference">
+    /// The direct reference or &lt;code&gt;null&lt;/code&gt; if not set.
+    /// </param>
+    /// <param name="indirectReference">
+    /// The indirect reference or &lt;code&gt;null&lt;/code&gt; if not set.
+    /// </param>
+    /// <param name="dataValueDescriptor">
+    /// The data value descriptor or &lt;code&gt;null&lt;/code&gt; if not
+    /// set.
+    /// </param>
+    /// <param name="externalData">
+    /// The external data in its encoded form.
+    /// </param>
+    constructor Create(const directReference: IDerObjectIdentifier;
+      const indirectReference: IDerInteger;
+      const dataValueDescriptor: IAsn1Object;
+      const externalData: IDerTaggedObject); overload;
+
+    constructor Create(const directReference: IDerObjectIdentifier;
+      const indirectReference: IDerInteger;
+      const dataValueDescriptor: IAsn1Object; encoding: Int32;
+      const externalData: IAsn1Object); overload;
+
+    procedure Encode(const derOut: TStream); override;
+
+    property dataValueDescriptor: IAsn1Object read GetDataValueDescriptor
+      write SetDataValueDescriptor;
+
+    property directReference: IDerObjectIdentifier read GetDirectReference
+      write SetDirectReference;
+
+    property encoding: Int32 read GetEncoding write SetEncoding;
+
+    property ExternalContent: IAsn1Object read GetExternalContent
+      write SetExternalContent;
+
+    property indirectReference: IDerInteger read GetIndirectReference
+      write SetIndirectReference;
+
+  end;
+
+type
+  TDerInteger = class sealed(TAsn1Object, IDerInteger)
+
+  strict private
+  var
+    Fbytes: TCryptoLibByteArray;
+
+    function GetBytes: TCryptoLibByteArray; inline;
+    function GetPositiveValue: TBigInteger; inline;
+    function GetValue: TBigInteger; inline;
+  strict protected
+    function Asn1GetHashCode(): Int32; override;
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+
+  public
+
+    constructor Create(Value: Int32); overload;
+    constructor Create(const Value: TBigInteger); overload;
+    constructor Create(const bytes: TCryptoLibByteArray); overload;
+
+    property Value: TBigInteger read GetValue;
+    property PositiveValue: TBigInteger read GetPositiveValue;
+    property bytes: TCryptoLibByteArray read GetBytes;
+
+    procedure Encode(const derOut: TStream); override;
+
+    function ToString(): String; override;
+
+    // /**
+    // * return an integer from the passed in object
+    // *
+    // * @exception ArgumentException if the object cannot be converted.
+    // */
+
+    class function GetInstance(const obj: TObject): IDerInteger;
+      overload; static;
+
+    // /**
+    // * return an Integer from a tagged object.
+    // *
+    // * @param obj the tagged object holding the object we want
+    // * @param isExplicit true if the object is meant to be explicitly
+    // *              tagged false otherwise.
+    // * @exception ArgumentException if the tagged object cannot
+    // *               be converted.
+    // */
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerInteger; overload; static; inline;
+
+  end;
+
+type
+  TDerExternalParser = class(TAsn1Encodable, IDerExternalParser)
+
+  strict private
+  var
+    F_parser: IAsn1StreamParser;
+
+  public
+
+    constructor Create(const parser: IAsn1StreamParser);
+    function ReadObject(): IAsn1Convertible; inline;
+    function ToAsn1Object(): IAsn1Object; override;
+
+  end;
+
+type
+  TDerOctetStringParser = class(TInterfacedObject, IAsn1OctetStringParser,
+    IAsn1Convertible, IDerOctetStringParser)
+
+  strict private
+  var
+    FStream: TStream;
+
+  public
+
+    constructor Create(stream: TStream);
+    destructor Destroy(); override;
+    function GetOctetStream(): TStream; inline;
+    function ToAsn1Object(): IAsn1Object;
+
+  end;
+
+type
+
+  TDerGeneralString = class(TDerStringBase, IDerGeneralString)
+
+  strict private
+  var
+    FStr: String;
+
+    function GetStr: String; inline;
+
+    property Str: String read GetStr;
+
+  strict protected
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+  public
+
+    constructor Create(const Str: TCryptoLibByteArray); overload;
+
+    constructor Create(const Str: String); overload;
+
+    function GetString(): String; override;
+
+    function GetOctets(): TCryptoLibByteArray; inline;
+
+    procedure Encode(const derOut: TStream); override;
+
+    class function GetInstance(const obj: TObject): IDerGeneralString; overload;
+      static; inline;
+
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerGeneralString; overload; static; inline;
+
+  end;
+
+type
+  TDerGenerator = class abstract(TAsn1Generator, IDerGenerator)
+
+  strict private
+  var
+    F_tagged, F_isExplicit: Boolean;
+    F_tagNo: Int32;
+
+    class procedure WriteLength(const outStr: TStream; length: Int32); static;
+
+  strict protected
+    constructor Create(const outStream: TStream); overload;
+    constructor Create(const outStream: TStream; tagNo: Int32;
+      isExplicit: Boolean); overload;
+
+  public
+    procedure WriteDerEncoded(tag: Int32;
+      const bytes: TCryptoLibByteArray); overload;
+    class procedure WriteDerEncoded(const outStream: TStream; tag: Int32;
+      const bytes: TCryptoLibByteArray); overload; static;
+
+    class procedure WriteDerEncoded(const outStr: TStream; tag: Int32;
+      const inStr: TStream); overload; static;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Der IA5String object - this is an ascii string.
+  /// </summary>
+  TDerIA5String = class(TDerStringBase, IDerIA5String)
+
+  strict private
+  var
+    FStr: String;
+
+    function GetStr: String; inline;
+    property Str: String read GetStr;
+
+  strict protected
+    function Asn1GetHashCode(): Int32; override;
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+  public
+
+    /// <summary>
+    /// basic constructor - with bytes.
+    /// </summary>
+    constructor Create(const Str: TCryptoLibByteArray); overload;
+
+    /// <summary>
+    /// basic constructor - without validation.
+    /// </summary>
+    constructor Create(const Str: String); overload;
+
+    /// <summary>
+    /// Constructor with optional validation.
+    /// </summary>
+    /// <param name="Str">
+    /// the base string to wrap.
+    /// </param>
+    /// <param name="validate">
+    /// whether or not to check the string.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if validate is true and the string contains characters that should
+    /// not be in an IA5String.
+    /// </exception>
+    constructor Create(const Str: String; validate: Boolean); overload;
+
+    function GetString(): String; override;
+
+    function GetOctets(): TCryptoLibByteArray; inline;
+
+    procedure Encode(const derOut: TStream); override;
+
+    /// <summary>
+    /// return a DerIA5String from the passed in object
+    /// </summary>
+    /// <param name="obj">
+    /// a DerIA5String or an object that can be converted into one.
+    /// </param>
+    /// <returns>
+    /// return a DerIA5String instance, or null.
+    /// </returns>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IDerIA5String; overload;
+      static; inline;
+
+    /// <summary>
+    /// return a DerIA5String from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// true if the object is meant to be explicitly tagged false otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerIA5String; overload; static; inline;
+
+    /// <summary>
+    /// return true if the passed in String can be represented without loss
+    /// as an IA5String, false otherwise.
+    /// </summary>
+    /// <param name="Str">
+    /// true if in printable set, false otherwise.
+    /// </param>
+    class function IsIA5String(const Str: String): Boolean; static; inline;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Der NumericString object - this is an ascii string of characters
+  /// {0,1,2,3,4,5,6,7,8,9, }.
+  /// </summary>
+  TDerNumericString = class(TDerStringBase, IDerNumericString)
+
+  strict private
+  var
+    FStr: String;
+
+    function GetStr: String; inline;
+    property Str: String read GetStr;
+
+  strict protected
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+  public
+
+    /// <summary>
+    /// basic constructor - with bytes.
+    /// </summary>
+    constructor Create(const Str: TCryptoLibByteArray); overload;
+
+    /// <summary>
+    /// basic constructor - without validation.
+    /// </summary>
+    constructor Create(const Str: String); overload;
+
+    /// <summary>
+    /// Constructor with optional validation.
+    /// </summary>
+    /// <param name="Str">
+    /// the base string to wrap.
+    /// </param>
+    /// <param name="validate">
+    /// whether or not to check the string.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if validate is true and the string contains characters that should
+    /// not be in an IA5String.
+    /// </exception>
+    constructor Create(const Str: String; validate: Boolean); overload;
+
+    function GetString(): String; override;
+
+    function GetOctets(): TCryptoLibByteArray; inline;
+
+    procedure Encode(const derOut: TStream); override;
+
+    /// <summary>
+    /// return a Numeric string from the passed in object
+    /// </summary>
+    /// <param name="obj">
+    /// a DerNumericString or an object that can be converted into one.
+    /// </param>
+    /// <returns>
+    /// return a DerNumericString instance, or null.
+    /// </returns>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IDerNumericString; overload;
+      static; inline;
+
+    /// <summary>
+    /// return a Numeric String from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// true if the object is meant to be explicitly tagged false otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerNumericString; overload; static; inline;
+
+    /// <summary>
+    /// Return true if the string can be represented as a NumericString
+    /// ('0'..'9', ' ')
+    /// </summary>
+    /// <param name="Str">
+    /// string to validate.
+    /// </param>
+    /// <returns>
+    /// true if numeric, false otherwise.
+    /// </returns>
+    class function IsNumericString(const Str: String): Boolean; static; inline;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Der PrintableString object.
+  /// </summary>
+  TDerPrintableString = class(TDerStringBase, IDerPrintableString)
+
+  strict private
+  var
+    FStr: String;
+
+    function GetStr: String; inline;
+
+  strict protected
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+  public
+
+    /// <summary>
+    /// basic constructor - with bytes.
+    /// </summary>
+    constructor Create(const Str: TCryptoLibByteArray); overload;
+
+    /// <summary>
+    /// basic constructor - without validation.
+    /// </summary>
+    constructor Create(const Str: String); overload;
+
+    /// <summary>
+    /// Constructor with optional validation.
+    /// </summary>
+    /// <param name="Str">
+    /// the base string to wrap.
+    /// </param>
+    /// <param name="validate">
+    /// whether or not to check the string.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if validate is true and the string contains characters that should
+    /// not be in an PrintableString.
+    /// </exception>
+    constructor Create(const Str: String; validate: Boolean); overload;
+
+    function GetString(): String; override;
+
+    function GetOctets(): TCryptoLibByteArray; inline;
+
+    procedure Encode(const derOut: TStream); override;
+
+    property Str: String read GetStr;
+
+    /// <summary>
+    /// return a printable string from the passed in object.
+    /// </summary>
+    /// <param name="obj">
+    /// a DerPrintableString or an object that can be converted into one.
+    /// </param>
+    /// <returns>
+    /// return a DerPrintableString instance, or null.
+    /// </returns>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IDerPrintableString;
+      overload; static; inline;
+
+    /// <summary>
+    /// return a Printable string from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// true if the object is meant to be explicitly tagged false otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerPrintableString; overload; static; inline;
+
+    /// <summary>
+    /// return true if the passed in String can be represented without loss
+    /// as a PrintableString, false otherwise.
+    /// </summary>
+    /// <param name="Str">
+    /// string to validate.
+    /// </param>
+    /// <returns>
+    /// return true if in printable set, false otherwise.
+    /// </returns>
+    class function IsPrintableString(const Str: String): Boolean;
+      static; inline;
+
+  end;
+
+type
+  TDerSequenceGenerator = class(TDerGenerator, IDerSequenceGenerator)
+
+  strict private
+  var
+    F_bOut: TMemoryStream;
+
+  public
+    constructor Create(outStream: TStream); overload;
+    constructor Create(outStream: TStream; tagNo: Int32;
+      isExplicit: Boolean); overload;
+    destructor Destroy(); override;
+    procedure AddObject(const obj: IAsn1Encodable); override;
+    function GetRawOutputStream(): TStream; override;
+    procedure Close(); override;
+  end;
+
+type
+
+  /// <summary>
+  /// Der T61String (also the teletex string) - 8-bit characters
+  /// </summary>
+  TDerT61String = class(TDerStringBase, IDerT61String)
+
+  strict private
+
+  var
+    FStr: String;
+
+    function GetStr: String; inline;
+    property Str: String read GetStr;
+
+    class function GetEncoding: TEncoding; static; inline;
+
+  strict protected
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+  public
+
+    /// <summary>
+    /// basic constructor - with bytes.
+    /// </summary>
+    constructor Create(const Str: TCryptoLibByteArray); overload;
+
+    /// <summary>
+    /// basic constructor
+    /// </summary>
+    constructor Create(const Str: String); overload;
+
+    function GetString(): String; override;
+
+    function GetOctets(): TCryptoLibByteArray; inline;
+
+    procedure Encode(const derOut: TStream); override;
+
+    /// <summary>
+    /// return a T61 string from the passed in object.
+    /// </summary>
+    /// <param name="obj">
+    /// a Der T61 string or an object that can be converted into one.
+    /// </param>
+    /// <returns>
+    /// return a Der T61 string instance, or null.
+    /// </returns>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IDerT61String; overload;
+      static; inline;
+
+    /// <summary>
+    /// return a Der T61 string from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// true if the object is meant to be explicitly tagged false otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerT61String; overload; static; inline;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Der UniversalString object.
+  /// </summary>
+  TDerUniversalString = class(TDerStringBase, IDerUniversalString)
+
+  strict private
+  var
+    FStr: TCryptoLibByteArray;
+
+  const
+    FTable: array [0 .. 15] of Char = ('0', '1', '2', '3', '4', '5', '6', '7',
+      '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
+
+    function GetStr: TCryptoLibByteArray; inline;
+    property Str: TCryptoLibByteArray read GetStr;
+
+  strict protected
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+  public
+
+    /// <summary>
+    /// basic constructor - byte encoded string.
+    /// </summary>
+    constructor Create(const Str: TCryptoLibByteArray); overload;
+
+    function GetString(): String; override;
+
+    function GetOctets(): TCryptoLibByteArray; inline;
+
+    procedure Encode(const derOut: TStream); override;
+
+    /// <summary>
+    /// return a Universal String from the passed in object.
+    /// </summary>
+    /// <param name="obj">
+    /// a Der T61 string or an object that can be converted into one.
+    /// </param>
+    /// <returns>
+    /// return a Der UniversalString instance, or null.
+    /// </returns>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IDerUniversalString;
+      overload; static; inline;
+
+    /// <summary>
+    /// return a Der UniversalString from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// true if the object is meant to be explicitly tagged false otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerUniversalString; overload; static; inline;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Der UTF8String object.
+  /// </summary>
+  TDerUtf8String = class(TDerStringBase, IDerUtf8String)
+
+  strict private
+  var
+    FStr: String;
+
+    function GetStr: String; inline;
+    property Str: String read GetStr;
+
+  strict protected
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+  public
+
+    /// <summary>
+    /// basic constructor - with bytes.
+    /// </summary>
+    constructor Create(const Str: TCryptoLibByteArray); overload;
+
+    /// <summary>
+    /// basic constructor
+    /// </summary>
+    constructor Create(const Str: String); overload;
+
+    function GetString(): String; override;
+
+    procedure Encode(const derOut: TStream); override;
+
+    /// <summary>
+    /// return an UTF8 string from the passed in object.
+    /// </summary>
+    /// <param name="obj">
+    /// a Der UTF8String or an object that can be converted into one.
+    /// </param>
+    /// <returns>
+    /// return a Der UTF8String instance, or null.
+    /// </returns>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IDerUtf8String; overload;
+      static; inline;
+
+    /// <summary>
+    /// return a Der UTF8String from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// true if the object is meant to be explicitly tagged false otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerUtf8String; overload; static; inline;
+
+  end;
+
+type
+  TDerVideotexString = class(TDerStringBase, IDerVideotexString)
+
+  strict private
+  var
+    FmString: TCryptoLibByteArray;
+
+    function GetmString: TCryptoLibByteArray; inline;
+
+  protected
+    function Asn1GetHashCode(): Int32; override;
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+
+  public
+    property mString: TCryptoLibByteArray read GetmString;
+
+    /// <summary>
+    /// basic constructor - with bytes.
+    /// </summary>
+    /// <param name="encoding">
+    /// the byte encoding of the characters making up the string.
+    /// </param>
+    constructor Create(const encoding: TCryptoLibByteArray);
+
+    function GetString(): String; override;
+
+    function GetOctets(): TCryptoLibByteArray; inline;
+
+    procedure Encode(const derOut: TStream); override;
+
+    /// <summary>
+    /// return a Videotex String from the passed in object
+    /// </summary>
+    /// <param name="obj">
+    /// a DerVideotexString or an object that can be converted into one.
+    /// </param>
+    /// <returns>
+    /// return a DerVideotexString instance, or null.
+    /// </returns>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IDerVideotexString;
+      overload; static; inline;
+
+    class function GetInstance(const obj: TCryptoLibByteArray)
+      : IDerVideotexString; overload; static;
+
+    /// <summary>
+    /// return a Videotex string from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// true if the object is meant to be explicitly tagged false otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerVideotexString; overload; static; inline;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Der VisibleString object.
+  /// </summary>
+  TDerVisibleString = class(TDerStringBase, IDerVisibleString)
+
+  strict private
+  var
+    FStr: String;
+
+    function GetStr: String; inline;
+    property Str: String read GetStr;
+
+  strict protected
+    function Asn1GetHashCode(): Int32; override;
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean; override;
+  public
+
+    /// <summary>
+    /// basic constructor - byte encoded string.
+    /// </summary>
+    constructor Create(const Str: TCryptoLibByteArray); overload;
+
+    /// <summary>
+    /// basic constructor
+    /// </summary>
+    constructor Create(const Str: String); overload;
+
+    function GetString(): String; override;
+
+    function GetOctets(): TCryptoLibByteArray; inline;
+
+    procedure Encode(const derOut: TStream); override;
+
+    /// <summary>
+    /// return a DerVisibleString from the passed in object
+    /// </summary>
+    /// <param name="obj">
+    /// a DerVisibleString or an object that can be converted into one.
+    /// </param>
+    /// <returns>
+    /// return a DerVisibleString instance, or null.
+    /// </returns>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: TObject): IDerVisibleString; overload;
+      static; inline;
+
+    /// <summary>
+    /// return a DerVisibleString from a tagged object.
+    /// </summary>
+    /// <param name="obj">
+    /// the tagged object holding the object we want
+    /// </param>
+    /// <param name="isExplicit">
+    /// true if the object is meant to be explicitly tagged false otherwise.
+    /// </param>
+    /// <exception cref="ClpCryptoLibTypes|EArgumentCryptoLibException">
+    /// if the tagged object cannot be converted.
+    /// </exception>
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      isExplicit: Boolean): IDerVisibleString; overload; static; inline;
+
+  end;
+
+implementation
+
+{ TStreamHelper }
+
+function TStreamHelper.ReadByte: Int32;
+var
+  buffer: TCryptoLibByteArray;
+begin
+  System.SetLength(buffer, 1);
+  if (TStreamSorter.Read(Self, buffer, 0, 1) = 0) then
+  begin
+    result := -1;
+  end
+  else
+  begin
+    result := Int32(buffer[0]);
+  end;
+end;
+
+procedure TStreamHelper.WriteByte(b: Byte);
+var
+  oneByteArray: TCryptoLibByteArray;
+begin
+  System.SetLength(oneByteArray, 1);
+  oneByteArray[0] := b;
+  // Self.Write(oneByteArray, 0, 1);
+  Self.Write(oneByteArray[0], 1);
+end;
+
+{ TStreamSorter }
+
+class function TStreamSorter.Read(input: TStream;
+  var buffer: TCryptoLibByteArray; offset, count: Int32): Int32;
+begin
+  if input is TIndefiniteLengthInputStream then
+  begin
+    result := (input as TIndefiniteLengthInputStream).
+      Read(buffer, offset, count);
+  end
+  else if input is TDefiniteLengthInputStream then
+
+  begin
+    result := (input as TDefiniteLengthInputStream).Read(buffer, offset, count);
+  end
+  else if input is TConstructedOctetStream then
+
+  begin
+    result := (input as TConstructedOctetStream).Read(buffer, offset, count);
+  end
+  else
+  begin
+    result := input.Read(buffer[offset], count);
+  end;
+end;
+
+class function TStreamSorter.ReadByte(input: TStream): Int32;
+begin
+  if input is TIndefiniteLengthInputStream then
+  begin
+    result := (input as TIndefiniteLengthInputStream).ReadByte();
+  end
+  else if input is TDefiniteLengthInputStream then
+
+  begin
+    result := (input as TDefiniteLengthInputStream).ReadByte();
+  end
+  else if input is TConstructedOctetStream then
+
+  begin
+    result := (input as TConstructedOctetStream).ReadByte();
+  end
+  else
+  begin
+    result := input.ReadByte();
+  end;
+end;
+
+{ TStreamUtils }
+
+class procedure TStreamUtils.Drain(const inStr: TStream);
+var
+  bs: TCryptoLibByteArray;
+begin
+  System.SetLength(bs, BufferSize);
+
+  while (TStreamSorter.Read(inStr, bs, 0, System.length(bs)) > 0) do
+  begin
+    // do nothing
+  end;
+end;
+
+class procedure TStreamUtils.PipeAll(const inStr, outStr: TStream);
+var
+  numRead: Int32;
+  bs: TCryptoLibByteArray;
+begin
+  System.SetLength(bs, BufferSize);
+
+  numRead := TStreamSorter.Read(inStr, bs, 0, System.length(bs));
+  while ((numRead) > 0) do
+  begin
+    outStr.Write(bs[0], numRead);
+    numRead := TStreamSorter.Read(inStr, bs, 0, System.length(bs));
+
+  end;
+end;
+
+class function TStreamUtils.PipeAllLimited(const inStr: TStream; limit: Int64;
+  const outStr: TStream): Int64;
+var
+  bs: TCryptoLibByteArray;
+  numRead: Int32;
+  total: Int64;
+begin
+  System.SetLength(bs, BufferSize);
+  total := 0;
+
+  numRead := TStreamSorter.Read(inStr, bs, 0, System.length(bs));
+  while ((numRead) > 0) do
+  begin
+    if ((limit - total) < numRead) then
+    begin
+      raise EStreamOverflowCryptoLibException.CreateRes(@SDataOverflow);
+    end;
+    total := total + numRead;
+    outStr.Write(bs[0], numRead);
+    numRead := TStreamSorter.Read(inStr, bs, 0, System.length(bs));
+
+  end;
+  result := total;
+end;
+
+class function TStreamUtils.ReadAll(const inStr: TStream): TCryptoLibByteArray;
+var
+  buf: TMemoryStream;
+begin
+  buf := TMemoryStream.Create();
+  try
+    PipeAll(inStr, buf);
+    System.SetLength(result, buf.Size);
+    buf.Position := 0;
+    buf.Read(result[0], buf.Size);
+  finally
+    buf.Free;
+  end;
+
+end;
+
+class function TStreamUtils.ReadAllLimited(const inStr: TStream; limit: Int32)
+  : TCryptoLibByteArray;
+var
+  buf: TMemoryStream;
+begin
+  buf := TMemoryStream.Create();
+  try
+    PipeAllLimited(inStr, limit, buf);
+    System.SetLength(result, buf.Size);
+    buf.Position := 0;
+    buf.Read(result[0], buf.Size);
+  finally
+    buf.Free;
+  end;
+
+end;
+
+class function TStreamUtils.ReadFully(const inStr: TStream;
+  var buf: TCryptoLibByteArray; off, len: Int32): Int32;
+var
+  totalRead, numRead: Int32;
+begin
+  totalRead := 0;
+
+  while (totalRead < len) do
+  begin
+
+    numRead := TStreamSorter.Read(inStr, buf, off + totalRead, len - totalRead);
+    if (numRead < 1) then
+    begin
+      break;
+    end;
+    totalRead := totalRead + numRead;
+  end;
+  result := totalRead;
+end;
+
+class function TStreamUtils.WriteBufTo(const buf: TMemoryStream;
+  const output: TCryptoLibByteArray; offset: Int32): Int32;
+var
+  bytes: TCryptoLibByteArray;
+begin
+  buf.Position := 0;
+  System.SetLength(bytes, buf.Size);
+  buf.Read(bytes[0], buf.Size);
+  System.Move(bytes[0], output[offset], System.length(bytes) *
+    System.SizeOf(Byte));
+  result := System.length(bytes);
+end;
+
+class procedure TStreamUtils.WriteZeroes(const outStr: TStream; count: Int64);
+var
+  zeroes: TCryptoLibByteArray;
+begin
+  System.SetLength(zeroes, BufferSize);
+  while (count > BufferSize) do
+  begin
+    outStr.Write(zeroes[0], BufferSize);
+    count := count - BufferSize;
+  end;
+  outStr.Write(zeroes[0], Int32(count));
+end;
+
+class function TStreamUtils.ReadFully(const inStr: TStream;
+  var buf: TCryptoLibByteArray): Int32;
+begin
+  result := ReadFully(inStr, buf, 0, System.length(buf));
+end;
+
+class procedure TStreamUtils.WriteBufTo(const buf: TMemoryStream;
+  const output: TStream);
+begin
+  output.CopyFrom(buf, buf.Size);
+end;
+
+{ TBaseInputStream }
+
+function TBaseInputStream.GetPosition: Int64;
+begin
+  raise ENotSupportedCryptoLibException.Create('');
+end;
+
+function TBaseInputStream.GetSize: Int64;
+begin
+  raise ENotSupportedCryptoLibException.Create('');
+end;
+
+{$IFNDEF _FIXINSIGHT_}
+
+function TBaseInputStream.Read(var buffer; count: LongInt): LongInt;
+begin
+  raise ENotSupportedCryptoLibException.Create('');
+end;
+
+function TBaseInputStream.Write(const buffer; count: LongInt): LongInt;
+begin
+  raise ENotSupportedCryptoLibException.Create('');
+end;
+{$ENDIF}
+
+function TBaseInputStream.ReadByte: Int32;
+var
+  buffer: TCryptoLibByteArray;
+begin
+  System.SetLength(buffer, 1);
+
+  // if (Read(Buffer, 0, 1) = 0) then
+  if (TStreamSorter.Read(Self, buffer, 0, 1) = 0) then
+  begin
+    result := -1;
+  end
+  else
+  begin
+    result := Int32(buffer[0]);
+  end;
+end;
+
+function TBaseInputStream.Seek(offset: LongInt; Origin: Word): LongInt;
+begin
+  result := Seek(Int64(offset), TSeekOrigin(Origin));
+end;
+
+{$IFNDEF _FIXINSIGHT_}
+
+function TBaseInputStream.Seek(const offset: Int64; Origin: TSeekOrigin): Int64;
+begin
+  raise ENotSupportedCryptoLibException.Create('');
+end;
+
+procedure TBaseInputStream.SetPosition(const Pos: Int64);
+begin
+  raise ENotSupportedCryptoLibException.Create('');
+end;
+
+{$ENDIF}
+
+procedure TBaseInputStream.SetSize(const NewSize: Int64);
+begin
+  SetSize(LongInt(NewSize));
+end;
+
+procedure TBaseInputStream.SetSize(NewSize: LongInt);
+begin
+  raise ENotSupportedCryptoLibException.Create('');
+end;
+
+procedure TBaseInputStream.SetSize64(const NewSize: Int64);
+begin
+  SetSize(NewSize);
+end;
+
+function TBaseInputStream.Read(buffer: TCryptoLibByteArray;
+  offset, count: LongInt): LongInt;
+var
+  &pos, endPoint, b: Int32;
+
+begin
+  Pos := offset;
+  try
+    endPoint := offset + count;
+    while (Pos < endPoint) do
+    begin
+      b := ReadByte();
+      if (b = -1) then
+      begin
+        break;
+      end;
+      buffer[Pos] := Byte(b);
+      System.Inc(Pos);
+    end;
+  except
+    on e: EIOCryptoLibException do
+    begin
+      if (Pos = offset) then
+        raise;
+    end;
+
+  end;
+
+  result := Pos - offset;
+end;
+
+{$IFNDEF _FIXINSIGHT_}
+
+function TBaseInputStream.Write(const buffer: TCryptoLibByteArray;
+  offset, count: LongInt): LongInt;
+begin
+  raise ENotSupportedCryptoLibException.Create('');
+end;
+
+{$ENDIF}
+{ TFilterStream }
+
+constructor TFilterStream.Create(const s: TStream);
+begin
+  inherited Create();
+  Fs := s;
+end;
+
+function TFilterStream.GetPosition: Int64;
+begin
+  result := Fs.Position;
+end;
+
+procedure TFilterStream.SetPosition(const Value: Int64);
+begin
+  Fs.Position := Value;
+end;
+
+function TFilterStream.Write(const buffer; count: LongInt): LongInt;
+begin
+  result := Fs.Write(PByte(buffer), count);
+end;
+
+procedure TFilterStream.WriteByte(Value: Byte);
+begin
+  Fs.WriteByte(Value);
+end;
+
+function TFilterStream.GetSize: Int64;
+begin
+  result := Fs.Size;
+end;
+
+function TFilterStream.Read(var buffer; count: LongInt): LongInt;
+begin
+  result := Fs.Read(PByte(buffer), count);
+end;
+
+function TFilterStream.ReadByte: Int32;
+begin
+
+  result := TStreamSorter.ReadByte(Fs);
+
+end;
+
+function TFilterStream.Seek(const offset: Int64; Origin: TSeekOrigin): Int64;
+begin
+  result := Fs.Seek(offset, Origin);
+end;
+
+{ TLimitedInputStream }
+
+constructor TLimitedInputStream.Create(inStream: TStream; limit: Int32);
+begin
+  Inherited Create();
+  F_in := inStream;
+  F_limit := limit;
+end;
+
+function TLimitedInputStream.GetRemaining: Int32;
+begin
+  // TODO: maybe one day this can become more accurate
+  result := F_limit;
+end;
+
+procedure TLimitedInputStream.SetParentEofDetect(&on: Boolean);
+var
+  indefiniteLengthInputStream: TIndefiniteLengthInputStream;
+begin
+
+  if F_in is TIndefiniteLengthInputStream then
+  begin
+    indefiniteLengthInputStream := F_in as TIndefiniteLengthInputStream;
+    indefiniteLengthInputStream.SetEofOn00(&on);
+  end;
+
+end;
+
+{ TDefiniteLengthInputStream }
+
+constructor TDefiniteLengthInputStream.Create(inStream: TStream; length: Int32);
+begin
+  Inherited Create(inStream, length);
+  if (length < 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidLength);
+  end;
+
+  F_originalLength := length;
+  F_remaining := length;
+
+  if (length = 0) then
+  begin
+    SetParentEofDetect(True);
+  end;
+end;
+
+class function TDefiniteLengthInputStream.GetEmptyBytes: TCryptoLibByteArray;
+begin
+  result := Nil;
+end;
+
+function TDefiniteLengthInputStream.GetRemaining: Int32;
+begin
+  result := F_remaining;
+end;
+
+function TDefiniteLengthInputStream.Read(buf: TCryptoLibByteArray;
+  off, len: LongInt): LongInt;
+var
+  toRead, numRead: Int32;
+
+begin
+  if (F_remaining = 0) then
+  begin
+    result := 0;
+    Exit;
+  end;
+
+  toRead := Min(len, F_remaining);
+
+  numRead := TStreamSorter.Read(F_in, buf, off, toRead);
+
+  if (numRead < 1) then
+  begin
+    raise EEndOfStreamCryptoLibException.CreateResFmt(@SEndOfStreamTwo,
+      [F_originalLength, F_remaining]);
+  end;
+  F_remaining := F_remaining - numRead;
+
+  if (F_remaining = 0) then
+  begin
+    SetParentEofDetect(True);
+  end;
+
+  result := numRead;
+end;
+
+procedure TDefiniteLengthInputStream.ReadAllIntoByteArray
+  (var buf: TCryptoLibByteArray);
+begin
+  if (F_remaining <> System.length(buf)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidBufferLength);
+  end;
+  F_remaining := F_remaining - TStreamUtils.ReadFully(F_in, buf);
+  if ((F_remaining <> 0)) then
+  begin
+    raise EEndOfStreamCryptoLibException.CreateResFmt(@SEndOfStreamTwo,
+      [F_originalLength, F_remaining]);
+  end;
+  SetParentEofDetect(True);
+end;
+
+function TDefiniteLengthInputStream.ReadByte: Int32;
+begin
+  if (F_remaining = 0) then
+  begin
+    result := -1;
+    Exit;
+  end;
+
+  // result := F_in.ReadByte();
+  result := TStreamSorter.ReadByte(F_in);
+
+  if (result < 0) then
+  begin
+    raise EEndOfStreamCryptoLibException.CreateResFmt(@SEndOfStreamTwo,
+      [F_originalLength, F_remaining]);
+  end;
+
+  System.Dec(F_remaining);
+  if (F_remaining = 0) then
+  begin
+    SetParentEofDetect(True);
+  end;
+
+end;
+
+function TDefiniteLengthInputStream.ToArray: TCryptoLibByteArray;
+var
+  bytes: TCryptoLibByteArray;
+begin
+  if (F_remaining = 0) then
+  begin
+    result := EmptyBytes;
+    Exit;
+  end;
+  System.SetLength(bytes, F_remaining);
+  F_remaining := F_remaining - TStreamUtils.ReadFully(F_in, bytes);
+  if (F_remaining <> 0) then
+  begin
+    raise EEndOfStreamCryptoLibException.CreateResFmt(@SEndOfStreamTwo,
+      [F_originalLength, F_remaining]);
+  end;
+  SetParentEofDetect(True);
+  result := bytes;
+end;
+
+{ TAsn1InputStream }
+
+class function TAsn1InputStream.FindLimit(const input: TStream): Int32;
+var
+  limitedInputStream: TLimitedInputStream;
+  mem: TMemoryStream;
+begin
+  limitedInputStream := input as TLimitedInputStream;
+  if (limitedInputStream <> Nil) then
+  begin
+    result := limitedInputStream.GetRemaining();
+    Exit;
+  end
+  else if (input is TMemoryStream) then
+  begin
+    mem := input as TMemoryStream;
+    result := Int32(mem.Size - mem.Position);
+    Exit;
+  end;
+
+  result := System.High(Int32);
+end;
+
+class function TAsn1InputStream.GetBuffer(const defIn
+  : TDefiniteLengthInputStream; const tmpBuffers: TCryptoLibMatrixByteArray)
+  : TCryptoLibByteArray;
+var
+  len: Int32;
+  buf, temp: TCryptoLibByteArray;
+begin
+  len := defIn.GetRemaining();
+  if (len >= System.length(tmpBuffers)) then
+  begin
+    result := defIn.ToArray();
+    Exit;
+  end;
+
+  buf := tmpBuffers[len];
+  if (buf = Nil) then
+  begin
+    System.SetLength(temp, len);
+    tmpBuffers[len] := temp;
+    buf := tmpBuffers[len];
+  end;
+
+  defIn.ReadAllIntoByteArray(buf);
+
+  result := buf;
+end;
+
+class function TAsn1InputStream.CreatePrimitiveDerObject(tagNo: Int32;
+  const defIn: TDefiniteLengthInputStream;
+  const tmpBuffers: TCryptoLibMatrixByteArray): IAsn1Object;
+var
+  bytes: TCryptoLibByteArray;
+begin
+  case tagNo of
+    TAsn1Tags.Boolean:
+      begin
+        result := TDerBoolean.FromOctetString(GetBuffer(defIn, tmpBuffers));
+        Exit;
+      end;
+    TAsn1Tags.Enumerated:
+      begin
+        result := TDerEnumerated.FromOctetString(GetBuffer(defIn, tmpBuffers));
+        Exit;
+      end;
+    TAsn1Tags.ObjectIdentifier:
+      begin
+        result := TDerObjectIdentifier.FromOctetString
+          (GetBuffer(defIn, tmpBuffers));
+        Exit;
+      end;
+
+  end;
+
+  bytes := defIn.ToArray();
+
+  case tagNo of
+
+    TAsn1Tags.BitString:
+      begin
+        result := TDerBitString.FromAsn1Octets(bytes);
+        Exit;
+      end;
+    TAsn1Tags.BmpString:
+      begin
+        result := TDerBmpString.Create(bytes);
+        Exit;
+      end;
+    // TAsn1Tags.GeneralizedTime:
+    // begin
+    // result := TDerGeneralizedTime.Create(bytes);
+    // Exit;
+    // end;
+    TAsn1Tags.GeneralString:
+      begin
+        result := TDerGeneralString.Create(bytes);
+        Exit;
+      end;
+    TAsn1Tags.GraphicString:
+      begin
+        result := TDerGraphicString.Create(bytes);
+        Exit;
+      end;
+    TAsn1Tags.IA5String:
+      begin
+        result := TDerIA5String.Create(bytes);
+        Exit;
+      end;
+    TAsn1Tags.Integer:
+      begin
+        result := TDerInteger.Create(bytes);
+        Exit;
+      end;
+    TAsn1Tags.Null:
+      begin
+        // actual content is ignored (enforce 0 length?)
+        result := TDerNull.Instance;
+        Exit;
+      end;
+    TAsn1Tags.NumericString:
+      begin
+        result := TDerNumericString.Create(bytes);
+        Exit;
+      end;
+    TAsn1Tags.OctetString:
+      begin
+        result := TDerOctetString.Create(bytes);
+        Exit;
+      end;
+    TAsn1Tags.PrintableString:
+      begin
+        result := TDerPrintableString.Create(bytes);
+        Exit;
+      end;
+    TAsn1Tags.T61String:
+      begin
+        result := TDerT61String.Create(bytes);
+        Exit;
+      end;
+    TAsn1Tags.UniversalString:
+      begin
+        result := TDerUniversalString.Create(bytes);
+        Exit;
+      end;
+    // TAsn1Tags.UtcTime:
+    // begin
+    // result := TDerUtcTime.Create(bytes);
+    // Exit;
+    // end;
+    TAsn1Tags.Utf8String:
+      begin
+        result := TDerUtf8String.Create(bytes);
+        Exit;
+      end;
+    TAsn1Tags.VideotexString:
+      begin
+        result := TDerVideotexString.Create(bytes);
+        Exit;
+      end;
+    TAsn1Tags.VisibleString:
+      begin
+        result := TDerVisibleString.Create(bytes);
+        Exit;
+      end;
+  else
+    begin
+      raise EIOCryptoLibException.CreateResFmt(@SUnknownTag, [tagNo]);
+    end;
+
+  end;
+end;
+
+destructor TAsn1InputStream.Destroy;
+begin
+  FStream.Free;
+  inherited Destroy;
+end;
+
+constructor TAsn1InputStream.Create(const inputStream: TStream; limit: Int32);
+begin
+  Inherited Create(inputStream);
+  Flimit := limit;
+  System.SetLength(FtmpBuffers, 16);
+end;
+
+constructor TAsn1InputStream.Create(const inputStream: TStream);
+begin
+  Create(inputStream, FindLimit(inputStream));
+end;
+
+constructor TAsn1InputStream.Create(const input: TCryptoLibByteArray);
+begin
+  // used TBytesStream here for one pass creation and population with byte array :)
+  FStream := TBytesStream.Create(input);
+  Create(FStream, System.length(input));
+
+end;
+
+class function TAsn1InputStream.ReadLength(const s: TStream;
+  limit: Int32): Int32;
+var
+  &length, Size, next, I: Int32;
+begin
+
+  length := TStreamSorter.ReadByte(s);
+
+  if (length < 0) then
+  begin
+    raise EEndOfStreamCryptoLibException.CreateRes(@SInvalidEnd);
+  end;
+
+  if (length = $80) then
+  begin
+    result := -1; // indefinite-length encoding
+    Exit;
+  end;
+
+  if (length > 127) then
+  begin
+    Size := length and $7F;
+
+    // Note: The invalid long form "$ff" (see X.690 8.1.3.5c) will be caught here
+    if (Size > 4) then
+    begin
+      raise EIOCryptoLibException.CreateResFmt(@SInvalidDerLength, [Size]);
+    end;
+
+    length := 0;
+    I := 0;
+    while I < Size do
+    begin
+
+      next := TStreamSorter.ReadByte(s);
+
+      if (next < 0) then
+      begin
+        raise EEndOfStreamCryptoLibException.CreateRes(@SEndOfStream);
+      end;
+
+      length := (length shl 8) + next;
+
+      System.Inc(I);
+    end;
+
+    if (length < 0) then
+    begin
+      raise EIOCryptoLibException.CreateRes(@SNegativeLength);
+    end;
+
+    if (length >= limit) then // after all we must have read at least 1 byte
+    begin
+      raise EIOCryptoLibException.CreateRes(@SOutOfBoundsLength);
+    end;
+  end;
+
+  result := length;
+end;
+
+function TAsn1InputStream.ReadObject: IAsn1Object;
+var
+  tag, tagNo, &length: Int32;
+  IsConstructed: Boolean;
+  indIn: TIndefiniteLengthInputStream;
+  sp: IAsn1StreamParser;
+begin
+
+  tag := ReadByte();
+
+  if (tag <= 0) then
+  begin
+    if (tag = 0) then
+    begin
+      raise EIOCryptoLibException.CreateRes(@SEndOfContent);
+    end;
+
+    result := Nil;
+    Exit;
+  end;
+
+  //
+  // calculate tag number
+  //
+  tagNo := ReadTagNumber(Fs, tag);
+
+  IsConstructed := (tag and TAsn1Tags.Constructed) <> 0;
+
+  //
+  // calculate length
+  //
+  length := ReadLength(Fs, Flimit);
+
+  if (length < 0) then // indefinite length method
+  begin
+    if (not IsConstructed) then
+    begin
+
+      raise EIOCryptoLibException.CreateRes(@SIndefiniteLength);
+
+    end;
+
+    indIn := TIndefiniteLengthInputStream.Create(Fs, Flimit);
+    sp := TAsn1StreamParser.Create(indIn, Flimit);
+
+    if ((tag and TAsn1Tags.Application) <> 0) then
+    begin
+      result := (TBerApplicationSpecificParser.Create(tagNo, sp)
+        as IBerApplicationSpecificParser).ToAsn1Object();
+      Exit;
+    end;
+
+    if ((tag and TAsn1Tags.Tagged) <> 0) then
+    begin
+      result := (TBerTaggedObjectParser.Create(True, tagNo, sp)
+        as IBerTaggedObjectParser).ToAsn1Object();
+      Exit;
+    end;
+
+    // TODO There are other tags that may be constructed (e.g. BitString)
+
+    case tagNo of
+      TAsn1Tags.OctetString:
+        begin
+          result := (TBerOctetStringParser.Create(sp) as IBerOctetStringParser)
+            .ToAsn1Object();
+          Exit;
+        end;
+      TAsn1Tags.Sequence:
+        begin
+          result := (TBerSequenceParser.Create(sp) as IBerSequenceParser)
+            .ToAsn1Object();
+          Exit;
+        end;
+      TAsn1Tags.&Set:
+        begin
+          result := (TBerSetParser.Create(sp) as IBerSetParser).ToAsn1Object();
+          Exit;
+        end;
+      TAsn1Tags.External:
+        begin
+          result := (TDerExternalParser.Create(sp) as IDerExternalParser)
+            .ToAsn1Object();
+          Exit;
+        end;
+    else
+      begin
+        raise EIOCryptoLibException.CreateRes(@SUnknownBerObject);
+      end;
+    end;
+
+  end
+  else
+  begin
+    try
+      result := BuildObject(tag, tagNo, length);
+    except
+      on e: EArgumentCryptoLibException do
+      begin
+        raise EAsn1CryptoLibException.CreateResFmt(@SCorruptedStream,
+          [e.Message]);
+      end;
+    end;
+  end;
+end;
+
+function TAsn1InputStream.BuildDerEncodableVector
+  (const dIn: TDefiniteLengthInputStream): IAsn1EncodableVector;
+var
+  res: TAsn1InputStream;
+begin
+  res := TAsn1InputStream.Create(dIn);
+  try
+    result := res.BuildEncodableVector();
+  finally
+    res.Free;
+  end;
+end;
+
+function TAsn1InputStream.BuildEncodableVector: IAsn1EncodableVector;
+var
+  v: IAsn1EncodableVector;
+  o: IAsn1Object;
+begin
+  v := TAsn1EncodableVector.Create();
+
+  o := ReadObject();
+  while (o <> Nil) do
+  begin
+    v.Add([o]);
+    o := ReadObject();
+  end;
+
+  result := v;
+end;
+
+function TAsn1InputStream.BuildObject(tag, tagNo, length: Int32): IAsn1Object;
+var
+  IsConstructed: Boolean;
+  defIn: TDefiniteLengthInputStream;
+  v: IAsn1EncodableVector;
+  strings: TList<IDerOctetString>;
+  I: Int32;
+begin
+  IsConstructed := (tag and TAsn1Tags.Constructed) <> 0;
+  defIn := TDefiniteLengthInputStream.Create(Fs, length);
+
+  if ((tag and TAsn1Tags.Application) <> 0) then
+  begin
+    try
+      result := TDerApplicationSpecific.Create(IsConstructed, tagNo,
+        defIn.ToArray());
+      Exit;
+    finally
+      defIn.Free;
+    end;
+  end;
+
+  if ((tag and TAsn1Tags.Tagged) <> 0) then
+  begin
+
+    result := (TAsn1StreamParser.Create(defIn) as IAsn1StreamParser)
+      .ReadTaggedObject(IsConstructed, tagNo);
+    Exit;
+
+  end;
+
+  if (IsConstructed) then
+  begin
+    // TODO There are other tags that may be constructed (e.g. BitString)
+    case (tagNo) of
+
+      TAsn1Tags.OctetString:
+        //
+        // yes, people actually do this...
+        //
+        begin
+          try
+            v := BuildDerEncodableVector(defIn);
+            strings := TList<IDerOctetString>.Create;
+            strings.capacity := v.count;
+
+            I := 0;
+            while (I <> v.count) do
+            begin
+              strings.Add(v[I] as IDerOctetString);
+            end;
+
+            result := TBerOctetString.Create(strings);
+            Exit;
+          finally
+            defIn.Free;
+          end;
+        end;
+      TAsn1Tags.Sequence:
+        begin
+          try
+            result := CreateDerSequence(defIn);
+            Exit;
+          finally
+            defIn.Free;
+          end;
+        end;
+      TAsn1Tags.&Set:
+        begin
+          try
+            result := CreateDerSet(defIn);
+            Exit;
+          finally
+            defIn.Free;
+          end;
+        end;
+      TAsn1Tags.External:
+        begin
+          try
+            result := TDerExternal.Create(BuildDerEncodableVector(defIn));
+            Exit;
+          finally
+            defIn.Free;
+          end;
+        end;
+    else
+      begin
+        defIn.Free; // free the stream incase an unsupported tag is encountered.
+        raise EIOCryptoLibException.CreateResFmt(@SUnknownTag, [tagNo]);
+      end;
+
+    end;
+
+  end;
+
+  try
+    result := CreatePrimitiveDerObject(tagNo, defIn, FtmpBuffers);
+  finally
+    defIn.Free;
+  end;
+
+end;
+
+function TAsn1InputStream.CreateDerSequence
+  (const dIn: TDefiniteLengthInputStream): IDerSequence;
+begin
+  result := TDerSequence.FromVector(BuildDerEncodableVector(dIn));
+end;
+
+function TAsn1InputStream.CreateDerSet(const dIn
+  : TDefiniteLengthInputStream): IDerSet;
+begin
+  result := TDerSet.FromVector(BuildDerEncodableVector(dIn), False);
+end;
+
+class function TAsn1InputStream.ReadTagNumber(const s: TStream;
+  tag: Int32): Int32;
+var
+  tagNo, b: Int32;
+begin
+  tagNo := tag and $1F;
+
+  //
+  // with tagged object tag number is bottom 5 bits, or stored at the start of the content
+  //
+  if (tagNo = $1F) then
+  begin
+    tagNo := 0;
+
+    b := TStreamSorter.ReadByte(s);
+
+    // X.690-0207 8.1.2.4.2
+    // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
+    if ((b and $7F) = 0) then // Note: -1 will pass
+    begin
+      raise EIOCryptoLibException.CreateRes(@SCorruptedStreamInvalidTag);
+    end;
+
+    while ((b >= 0) and ((b and $80) <> 0)) do
+    begin
+      tagNo := tagNo or (b and $7F);
+      tagNo := tagNo shl 7;
+
+      b := TStreamSorter.ReadByte(s);
+
+    end;
+
+    if (b < 0) then
+    begin
+      raise EEndOfStreamCryptoLibException.CreateRes(@SEOFFound);
+    end;
+    tagNo := tagNo or (b and $7F);
+  end;
+
+  result := tagNo;
+end;
+
+{ TDerOutputStream }
+
+constructor TDerOutputStream.Create(const os: TStream);
+begin
+  Inherited Create(os);
+end;
+
+procedure TDerOutputStream.WriteEncoded(tag: Int32; first: Byte;
+  const bytes: TCryptoLibByteArray);
+begin
+  WriteByte(Byte(tag));
+  WriteLength(System.length(bytes) + 1);
+  WriteByte(first);
+  Write(bytes[0], System.length(bytes));
+end;
+
+procedure TDerOutputStream.WriteEncoded(tag: Int32;
+  const bytes: TCryptoLibByteArray);
+begin
+  WriteByte(Byte(tag));
+  WriteLength(System.length(bytes));
+  if bytes <> Nil then
+  begin
+    Write(bytes[0], System.length(bytes));
+  end;
+end;
+
+procedure TDerOutputStream.WriteEncoded(flags, tagNo: Int32;
+  const bytes: TCryptoLibByteArray);
+begin
+  WriteTag(flags, tagNo);
+  WriteLength(System.length(bytes));
+  Write(bytes[0], System.length(bytes));
+end;
+
+procedure TDerOutputStream.WriteEncoded(tag: Int32;
+  const bytes: TCryptoLibByteArray; offset, length: Int32);
+begin
+  WriteByte(Byte(tag));
+  WriteLength(length);
+  Write(bytes[offset], length);
+end;
+
+procedure TDerOutputStream.WriteLength(length: Int32);
+var
+  Size, I: Int32;
+  val: UInt32;
+begin
+  if (length > 127) then
+  begin
+    Size := 1;
+    val := UInt32(length);
+    val := val shr 8;
+    while (val <> 0) do
+    begin
+      System.Inc(Size);
+      val := val shr 8;
+    end;
+
+    WriteByte(Byte(Size or $80));
+
+    I := (Size - 1) * 8;
+
+    while I >= 0 do
+    begin
+      WriteByte(Byte(TBits.Asr32(length, I)));
+      System.Dec(I, 8);
+    end;
+
+  end
+  else
+  begin
+    WriteByte(Byte(length));
+  end;
+end;
+
+procedure TDerOutputStream.WriteNull;
+begin
+  WriteByte(TAsn1Tags.Null);
+  WriteByte($00);
+end;
+
+procedure TDerOutputStream.WriteObject(const obj: IAsn1Encodable);
+var
+  asn1: IAsn1Object;
+begin
+  if (obj = Nil) then
+  begin
+    WriteNull();
+  end
+  else
+  begin
+    asn1 := obj.ToAsn1Object();
+    asn1.Encode(Self);
+  end;
+end;
+
+procedure TDerOutputStream.WriteObject(const obj: IAsn1Object);
+begin
+  if (obj = Nil) then
+  begin
+    WriteNull();
+  end
+  else
+  begin
+    obj.Encode(Self);
+  end;
+end;
+
+procedure TDerOutputStream.WriteTag(flags, tagNo: Int32);
+var
+  stack: TCryptoLibByteArray;
+  Pos: Int32;
+begin
+  if (tagNo < 31) then
+  begin
+    WriteByte(Byte(flags or tagNo));
+  end
+  else
+  begin
+    WriteByte(Byte(flags or $1F));
+    if (tagNo < 128) then
+    begin
+      WriteByte(Byte(tagNo));
+    end
+    else
+    begin
+      System.SetLength(stack, 5);
+      Pos := System.length(stack);
+
+      System.Dec(Pos);
+      stack[Pos] := Byte(tagNo and $7F);
+
+      repeat
+        tagNo := TBits.Asr32(tagNo, 7);
+        System.Dec(Pos);
+        stack[Pos] := Byte(tagNo and $7F or $80);
+      until (not(tagNo > 127));
+
+      Write(stack[Pos], System.length(stack) - Pos);
+    end;
+  end;
+end;
+
+{ TAsn1OutputStream }
+
+constructor TAsn1OutputStream.Create(os: TStream);
+begin
+  Inherited Create(os);
+end;
+
+{ TBerOutputStream }
+
+constructor TBerOutputStream.Create(os: TStream);
+begin
+  Inherited Create(os);
+end;
+
+{ TConstructedOctetStream }
+
+constructor TConstructedOctetStream.Create(const parser: IAsn1StreamParser);
+begin
+  Inherited Create();
+  F_parser := parser;
+  F_first := True;
+end;
+
+function TConstructedOctetStream.Read(buffer: TCryptoLibByteArray;
+  offset, count: LongInt): LongInt;
+var
+  s, aos: IAsn1OctetStringParser;
+  totalRead, numRead: Int32;
+begin
+  if (F_currentStream = Nil) then
+  begin
+    if (not F_first) then
+    begin
+      result := 0;
+      Exit;
+    end;
+
+    if (not Supports(F_parser.ReadObject(), IAsn1OctetStringParser, s)) then
+    begin
+      result := 0;
+      Exit;
+    end;
+
+    F_first := False;
+    F_currentStream := s.GetOctetStream();
+  end;
+
+  totalRead := 0;
+
+  while True do
+
+  begin
+
+    numRead := TStreamSorter.Read(F_currentStream, buffer, offset + totalRead,
+      count - totalRead);
+
+    if (numRead > 0) then
+    begin
+      totalRead := totalRead + numRead;
+
+      if (totalRead = count) then
+      begin
+        result := totalRead;
+        Exit;
+      end;
+    end
+    else
+    begin
+
+      if (not Supports(F_parser.ReadObject(), IAsn1OctetStringParser, aos)) then
+      begin
+        F_currentStream := Nil;
+        result := totalRead;
+        Exit;
+      end;
+
+      F_currentStream := aos.GetOctetStream();
+    end
+  end;
+  result := 0;
+end;
+
+function TConstructedOctetStream.ReadByte: Int32;
+var
+  s, aos: IAsn1OctetStringParser;
+  b: Int32;
+begin
+  if (F_currentStream = Nil) then
+  begin
+    if (not F_first) then
+    begin
+      result := 0;
+      Exit;
+    end;
+
+    if (not Supports(F_parser.ReadObject(), IAsn1OctetStringParser, s)) then
+    begin
+      result := 0;
+      Exit;
+    end;
+
+    F_first := False;
+    F_currentStream := s.GetOctetStream();
+  end;
+
+  while True do
+
+  begin
+
+    // b := F_currentStream.ReadByte();
+    b := TStreamSorter.ReadByte(F_currentStream);
+
+    if (b >= 0) then
+    begin
+      result := b;
+      Exit;
+    end;
+
+    if (not Supports(F_parser.ReadObject(), IAsn1OctetStringParser, aos)) then
+    begin
+      F_currentStream := Nil;
+      result := -1;
+      Exit;
+    end;
+
+    F_currentStream := aos.GetOctetStream();
+  end;
+
+  result := 0;
+end;
+
+{ TIndefiniteLengthInputStream }
+
+function TIndefiniteLengthInputStream.RequireByte: Int32;
+begin
+
+  // result := F_in.ReadByte();
+  result := TStreamSorter.ReadByte(F_in);
+
+  if (result < 0) then
+  begin
+    // Corrupted stream
+    raise EEndOfStreamCryptoLibException.Create('');
+  end;
+end;
+
+function TIndefiniteLengthInputStream.CheckForEof: Boolean;
+var
+  extra: Int32;
+begin
+  if (F_lookAhead = $00) then
+  begin
+    extra := RequireByte();
+    if (extra <> 0) then
+    begin
+      raise EIOCryptoLibException.CreateRes(@SMalformedContent);
+    end;
+
+    F_lookAhead := -1;
+    SetParentEofDetect(True);
+    result := True;
+    Exit;
+  end;
+  result := F_lookAhead < 0;
+end;
+
+constructor TIndefiniteLengthInputStream.Create(inStream: TStream;
+  limit: Int32);
+begin
+  Inherited Create(inStream, limit);
+  F_lookAhead := RequireByte();
+  CheckForEof();
+end;
+
+function TIndefiniteLengthInputStream.Read(buffer: TCryptoLibByteArray;
+  offset, count: LongInt): LongInt;
+var
+  numRead: Int32;
+begin
+  // Only use this optimisation if we aren't checking for 00
+  if ((F_eofOn00) or (count <= 1)) then
+  begin
+    result := (Inherited Read(buffer, offset, count));
+    Exit;
+  end;
+
+  if (F_lookAhead < 0) then
+  begin
+    result := 0;
+    Exit;
+  end;
+
+  numRead := TStreamSorter.Read(F_in, buffer, offset + 1, count - 1);
+
+  if (numRead <= 0) then
+  begin
+    // Corrupted stream
+    raise EEndOfStreamCryptoLibException.Create('');
+  end;
+
+  buffer[offset] := Byte(F_lookAhead);
+  F_lookAhead := RequireByte();
+
+  result := numRead + 1;
+end;
+
+function TIndefiniteLengthInputStream.ReadByte: Int32;
+begin
+  if (F_eofOn00 and CheckForEof()) then
+  begin
+    result := -1;
+    Exit;
+  end;
+
+  result := F_lookAhead;
+  F_lookAhead := RequireByte();
+
+end;
+
+procedure TIndefiniteLengthInputStream.SetEofOn00(eofOn00: Boolean);
+begin
+  F_eofOn00 := eofOn00;
+  if (F_eofOn00) then
+  begin
+    CheckForEof();
+  end;
+end;
+
+{ TCollectionUtilities }
+
+class function TCollectionUtilities.ToStructuredString
+  (c: TList<IAsn1Encodable>): String;
+var
+  sl: TStringList;
+  idx: Int32;
+begin
+
+  sl := TStringList.Create();
+  sl.LineBreak := '';
+  try
+    sl.Add('[');
+
+    if (c.count <> 0) then
+    begin
+      sl.Add((c[0] as TAsn1Encodable).ClassName);
+      if c.count > 1 then
+      begin
+        for idx := 1 to c.count - 2 do
+        begin
+          sl.Add(', ');
+          sl.Add((c[idx] as TAsn1Encodable).ClassName);
+        end;
+      end;
+    end;
+
+    sl.Add(']');
+    result := sl.Text;
+  finally
+    sl.Free;
+  end;
+
+end;
+
+{ TAsn1Encodable }
+
+function TAsn1Encodable.Equals(const other: IAsn1Convertible): Boolean;
+var
+  o1, o2: IAsn1Object;
+begin
+
+  if (other = Self as IAsn1Convertible) then
+  begin
+    result := True;
+    Exit;
+  end;
+
+  if (other = Nil) then
+  begin
+    result := False;
+    Exit;
+  end;
+  o1 := ToAsn1Object();
+  o2 := other.ToAsn1Object();
+
+  result := ((o1 = o2) or o1.CallAsn1Equals(o2));
+end;
+
+function TAsn1Encodable.GetDerEncoded: TCryptoLibByteArray;
+begin
+
+  try
+    result := GetEncoded(Der);
+  except
+    on e: EIOCryptoLibException do
+    begin
+      result := Nil;
+    end;
+  end;
+end;
+
+function TAsn1Encodable.GetEncoded: TCryptoLibByteArray;
+var
+  bOut: TMemoryStream;
+  aOut: TAsn1OutputStream;
+begin
+
+  bOut := TMemoryStream.Create();
+  aOut := TAsn1OutputStream.Create(bOut);
+  try
+    aOut.WriteObject(Self as IAsn1Encodable);
+    System.SetLength(result, bOut.Size);
+    bOut.Position := 0;
+    bOut.Read(result[0], System.length(result));
+
+  finally
+    bOut.Free;
+    aOut.Free;
+  end;
+
+end;
+
+function TAsn1Encodable.GetEncoded(const encoding: String): TCryptoLibByteArray;
+var
+  bOut: TMemoryStream;
+  dOut: TDerOutputStream;
+begin
+  if (encoding = Der) then
+  begin
+    bOut := TMemoryStream.Create();
+    dOut := TDerOutputStream.Create(bOut);
+    try
+      dOut.WriteObject(Self as IAsn1Encodable);
+      System.SetLength(result, bOut.Size);
+      bOut.Position := 0;
+      bOut.Read(result[0], System.length(result));
+
+    finally
+      bOut.Free;
+      dOut.Free;
+    end;
+    Exit;
+  end;
+
+  result := GetEncoded();
+end;
+
+function TAsn1Encodable.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := ToAsn1Object().CallAsn1GetHashCode();
+end;
+
+{ TAsn1Object }
+
+function TAsn1Object.CallAsn1Equals(const obj: IAsn1Object): Boolean;
+begin
+  result := Asn1Equals(obj);
+end;
+
+function TAsn1Object.CallAsn1GetHashCode: Int32;
+begin
+  result := Asn1GetHashCode();
+end;
+
+class function TAsn1Object.FromByteArray(const data: TCryptoLibByteArray)
+  : IAsn1Object;
+var
+  asn1: TAsn1InputStream;
+  input: TBytesStream;
+begin
+  try
+    // used TBytesStream here for one pass creation and population with byte array :)
+    input := TBytesStream.Create(data);
+    try
+
+      asn1 := TAsn1InputStream.Create(input, System.length(data));
+
+      try
+        result := asn1.ReadObject();
+      finally
+        asn1.Free;
+      end;
+      if (input.Position <> input.Size) then
+      begin
+        raise EIOCryptoLibException.CreateRes(@SExtraData);
+      end;
+    finally
+      input.Free;
+    end;
+  except
+    on e: EInvalidCastCryptoLibException do
+    begin
+      raise EIOCryptoLibException.CreateRes(@SUnRecognizedObjectByteArray);
+    end;
+  end;
+end;
+
+class function TAsn1Object.FromStream(const inStr: TStream): IAsn1Object;
+var
+  asn1Stream: TAsn1InputStream;
+begin
+  asn1Stream := TAsn1InputStream.Create(inStr);
+  try
+    try
+      result := asn1Stream.ReadObject();
+    except
+      on e: EInvalidCastCryptoLibException do
+      begin
+        raise EIOCryptoLibException.CreateRes(@SUnRecognizedObjectStream);
+      end;
+    end;
+  finally
+    asn1Stream.Free;
+  end;
+end;
+
+function TAsn1Object.ToAsn1Object: IAsn1Object;
+begin
+  result := Self as IAsn1Object;
+end;
+
+{ TDerObjectIdentifier }
+
+function TDerObjectIdentifier.GetID: String;
+begin
+  result := Fidentifier;
+end;
+
+function TDerObjectIdentifier.Asn1Equals(const asn1Object: IAsn1Object)
+  : Boolean;
+var
+  other: IDerObjectIdentifier;
+begin
+  if (not Supports(asn1Object, IDerObjectIdentifier, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := ID = other.ID;
+end;
+
+function TDerObjectIdentifier.Asn1GetHashCode: Int32;
+begin
+  result := TStringUtils.GetStringHashCode(Fidentifier);
+end;
+
+class procedure TDerObjectIdentifier.Boot;
+begin
+  if FLock = Nil then
+  begin
+    FLock := TCriticalSection.Create;
+  end;
+end;
+
+function TDerObjectIdentifier.Branch(const branchID: String)
+  : IDerObjectIdentifier;
+begin
+  result := TDerObjectIdentifier.Create(Self as IDerObjectIdentifier, branchID);
+end;
+
+constructor TDerObjectIdentifier.Create(const oid: IDerObjectIdentifier;
+  const branchID: String);
+begin
+  Inherited Create();
+  if (not(IsValidBranchID(branchID, 1))) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SInvalidBranchId,
+      [branchID]);
+  end;
+
+  Fidentifier := oid.ID + '.' + branchID;
+end;
+
+constructor TDerObjectIdentifier.Create(const identifier: String);
+begin
+  Inherited Create();
+  if (identifier = '') then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SIdentifierNil);
+  end;
+  if (not(IsValidIdentifier(identifier))) then
+  begin
+    raise EFormatCryptoLibException.CreateResFmt(@SInvalidOID, [identifier]);
+  end;
+
+  Fidentifier := identifier;
+end;
+
+constructor TDerObjectIdentifier.Create(const bytes: TCryptoLibByteArray);
+begin
+  Inherited Create();
+  Fidentifier := MakeOidStringFromBytes(bytes);
+  Fbody := System.Copy(bytes);
+end;
+
+function TDerObjectIdentifier.&on(const stem: IDerObjectIdentifier): Boolean;
+var
+  LocalId, stemId: String;
+begin
+  LocalId := ID;
+  stemId := stem.ID;
+  result := (System.length(LocalId) > System.length(stemId)) and
+    (LocalId[System.length(stemId) + 1] = '.') and
+    (AnsiStartsStr(stemId, LocalId));
+end;
+
+class constructor TDerObjectIdentifier.CreateDerObjectIdentifier;
+begin
+  TDerObjectIdentifier.Boot;
+end;
+
+class destructor TDerObjectIdentifier.DestroyDerObjectIdentifier;
+begin
+  FLock.Free;
+end;
+
+procedure TDerObjectIdentifier.DoOutput(const bOut: TMemoryStream);
+var
+  tok: IOidTokenizer;
+  token: String;
+  first: Int32;
+begin
+  tok := TOidTokenizer.Create(Fidentifier);
+  token := tok.NextToken();
+  first := StrToInt(token) * 40;
+  token := tok.NextToken();
+  if (System.length(token) <= 18) then
+  begin
+    WriteField(bOut, Int64(first + StrToInt64(token)));
+  end
+  else
+  begin
+    WriteField(bOut, TBigInteger.Create(token).Add(TBigInteger.ValueOf(first)));
+  end;
+
+  while (tok.HasMoreTokens) do
+  begin
+    token := tok.NextToken();
+    if (System.length(token) <= 18) then
+    begin
+      WriteField(bOut, StrToInt64(token));
+    end
+    else
+    begin
+      WriteField(bOut, TBigInteger.Create(token));
+    end;
+  end;
+end;
+
+procedure TDerObjectIdentifier.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.ObjectIdentifier,
+    GetBody());
+end;
+
+class function TDerObjectIdentifier.FromOctetString
+  (const enc: TCryptoLibByteArray): IDerObjectIdentifier;
+var
+  HashCode, first: Int32;
+  entry: IDerObjectIdentifier;
+begin
+
+  HashCode := TArrayUtils.GetArrayHashCode(enc);
+  first := HashCode and 1023;
+
+  FLock.Acquire;
+  try
+    entry := Fcache[first];
+    if ((entry <> Nil) and (TArrayUtils.AreEqual(enc, entry.GetBody()))) then
+    begin
+      result := entry;
+      Exit;
+    end;
+
+    Fcache[first] := TDerObjectIdentifier.Create(enc);
+    result := Fcache[first];
+
+  finally
+    FLock.Release;
+  end;
+
+end;
+
+function TDerObjectIdentifier.GetBody: TCryptoLibByteArray;
+var
+  bOut: TMemoryStream;
+begin
+
+  FLock.Acquire;
+  try
+    if (Fbody = Nil) then
+    begin
+      bOut := TMemoryStream.Create();
+      try
+        DoOutput(bOut);
+        System.SetLength(Fbody, bOut.Size);
+        bOut.Position := 0;
+        bOut.Read(Fbody[0], System.length(Fbody));
+      finally
+        bOut.Free;
+      end;
+    end;
+
+  finally
+    FLock.Release;
+  end;
+
+  result := Fbody;
+end;
+
+class function TDerObjectIdentifier.GetInstance(const obj: IAsn1TaggedObject;
+  explicitly: Boolean): IDerObjectIdentifier;
+var
+  o: IAsn1Object;
+begin
+
+  o := obj.GetObject();
+
+  if ((explicitly) or (Supports(o, IDerObjectIdentifier))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := FromOctetString(TAsn1OctetString.GetInstance(o as TAsn1Object)
+    .GetOctets());
+end;
+
+class function TDerObjectIdentifier.GetInstance(const obj: TObject)
+  : IDerObjectIdentifier;
+begin
+  if ((obj = Nil) or (obj is TDerObjectIdentifier)) then
+  begin
+    result := obj as TDerObjectIdentifier;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerObjectIdentifier.GetInstance(const obj: TCryptoLibByteArray)
+  : IDerObjectIdentifier;
+begin
+  result := FromOctetString(obj);
+end;
+
+class function TDerObjectIdentifier.IsValidBranchID(const branchID: String;
+  start: Int32): Boolean;
+var
+  periodAllowed: Boolean;
+  Pos: Int32;
+  ch: Char;
+begin
+  periodAllowed := False;
+
+  Pos := System.length(branchID) + 1;
+  System.Dec(Pos);
+  while (Pos >= start) do
+  begin
+    ch := branchID[Pos];
+
+    // TODO Leading zeroes?
+    // if (('0' <= ch) and (ch <= '9')) then
+    // begin
+    // periodAllowed := true;
+    // continue;
+    // end;
+
+    // TODO Leading zeroes?
+    if (CharInSet(ch, ['0' .. '9'])) then
+    begin
+      periodAllowed := True;
+      System.Dec(Pos);
+      continue;
+    end;
+
+    if (ch = '.') then
+    begin
+      if (not(periodAllowed)) then
+      begin
+        result := False;
+        Exit;
+      end;
+
+      periodAllowed := False;
+      System.Dec(Pos);
+      continue;
+    end;
+
+    result := False;
+    Exit;
+  end;
+
+  result := periodAllowed;
+end;
+
+class function TDerObjectIdentifier.IsValidIdentifier(const identifier
+  : String): Boolean;
+var
+  first: Char;
+begin
+  if ((System.length(identifier) < 3) or (identifier[2] <> '.')) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  first := identifier[1];
+  // if ((first < '0') or (first > '2')) then
+  // begin
+  // result := false;
+  // Exit;
+  // end;
+  if (not CharInSet(first, ['0' .. '2'])) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := IsValidBranchID(identifier, 3);
+end;
+
+class function TDerObjectIdentifier.MakeOidStringFromBytes
+  (const bytes: TCryptoLibByteArray): String;
+var
+  objId: TStringList;
+  Value: Int64;
+  bigValue: TBigInteger;
+  first: Boolean;
+  I, b: Int32;
+begin
+  Value := 0;
+  bigValue := Default (TBigInteger);
+  first := True;
+  objId := TStringList.Create();
+  objId.LineBreak := '';
+  try
+    I := 0;
+    while I <> System.length(bytes) do
+    begin
+      b := Int32(bytes[I]);
+
+      if (Value <= LONG_LIMIT) then
+      begin
+        Value := Value + (b and $7F);
+        if ((b and $80) = 0) then // end of number reached
+        begin
+          if (first) then
+          begin
+            if (Value < 40) then
+            begin
+              objId.Add('0');
+            end
+            else if (Value < 80) then
+            begin
+              objId.Add('1');
+              Value := Value - 40;
+            end
+            else
+            begin
+              objId.Add('2');
+              Value := Value - 80;
+            end;
+            first := False;
+          end;
+
+          objId.Add('.');
+          objId.Add(IntToStr(Value));
+          Value := 0;
+        end
+        else
+        begin
+          Value := Value shl 7;
+        end;
+      end
+      else
+      begin
+        if (not bigValue.IsInitialized) then
+        begin
+          bigValue := TBigInteger.ValueOf(Value);
+        end;
+        bigValue := bigValue.&Or(TBigInteger.ValueOf(b and $7F));
+        if ((b and $80) = 0) then
+        begin
+          if (first) then
+          begin
+            objId.Add('2');
+            bigValue := bigValue.Subtract(TBigInteger.ValueOf(80));
+            first := False;
+          end;
+
+          objId.Add('.');
+          objId.Add(bigValue.ToString());
+          bigValue := Default (TBigInteger);
+          Value := 0;
+        end
+        else
+        begin
+          bigValue := bigValue.ShiftLeft(7);
+        end
+      end;
+
+      System.Inc(I);
+    end;
+
+    result := objId.Text;
+
+  finally
+    objId.Free;
+  end;
+
+end;
+
+function TDerObjectIdentifier.ToString: String;
+begin
+  result := ID;
+end;
+
+procedure TDerObjectIdentifier.WriteField(const outputStream: TStream;
+  const fieldValue: TBigInteger);
+var
+  byteCount, I: Int32;
+  tmpValue: TBigInteger;
+  tmp: TCryptoLibByteArray;
+begin
+  byteCount := (fieldValue.BitLength + 6) div 7;
+  if (byteCount = 0) then
+  begin
+    outputStream.WriteByte(0);
+  end
+  else
+  begin
+    tmpValue := fieldValue;
+    System.SetLength(tmp, byteCount);
+
+    I := byteCount - 1;
+
+    while I >= 0 do
+    begin
+      tmp[I] := Byte((tmpValue.Int32Value and $7F) or $80);
+      tmpValue := tmpValue.ShiftRight(7);
+      System.Dec(I);
+    end;
+
+    tmp[byteCount - 1] := tmp[byteCount - 1] and $7F;
+    outputStream.Write(tmp[0], System.length(tmp));
+  end;
+end;
+
+procedure TDerObjectIdentifier.WriteField(const outputStream: TStream;
+  fieldValue: Int64);
+var
+  tempRes: TCryptoLibByteArray;
+  Pos: Int32;
+begin
+  System.SetLength(tempRes, 9);
+  Pos := 8;
+  tempRes[Pos] := Byte(fieldValue and $7F);
+  while (fieldValue >= (Int64(1) shl 7)) do
+  begin
+    fieldValue := TBits.Asr64(fieldValue, 7);
+    System.Dec(Pos);
+    tempRes[Pos] := Byte((fieldValue and $7F) or $80);
+  end;
+  outputStream.Write(tempRes[Pos], 9 - Pos);
+end;
+
+{ TAsn1EncodableVector }
+
+procedure TAsn1EncodableVector.Add(const objs: array of IAsn1Encodable);
+var
+  obj: IAsn1Encodable;
+begin
+  for obj in objs do
+  begin
+    Flist.Add(obj);
+  end;
+end;
+
+procedure TAsn1EncodableVector.AddOptional(const objs: array of IAsn1Encodable);
+var
+  obj: IAsn1Encodable;
+begin
+  if (System.length(objs) <> 0) then
+  begin
+    for obj in objs do
+    begin
+      if (obj <> Nil) then
+      begin
+        Flist.Add(obj);
+      end;
+    end;
+  end;
+end;
+
+constructor TAsn1EncodableVector.Create(const v: array of IAsn1Encodable);
+begin
+  inherited Create();
+  Flist := TList<IAsn1Encodable>.Create();
+  Add(v);
+end;
+
+constructor TAsn1EncodableVector.Create();
+begin
+  inherited Create();
+  Flist := TList<IAsn1Encodable>.Create();
+end;
+
+destructor TAsn1EncodableVector.Destroy;
+begin
+  Flist.Free;
+  inherited Destroy;
+end;
+
+class function TAsn1EncodableVector.FromEnumerable
+  (const e: TList<IAsn1Encodable>): IAsn1EncodableVector;
+var
+  v: IAsn1EncodableVector;
+  obj: IAsn1Encodable;
+begin
+  v := TAsn1EncodableVector.Create();
+  for obj in e do
+  begin
+    v.Add(obj);
+  end;
+  result := v;
+end;
+
+function TAsn1EncodableVector.GetCount: Int32;
+begin
+  result := Flist.count;
+end;
+
+function TAsn1EncodableVector.GetEnumerable
+  : TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  result := Flist.ToArray;
+end;
+
+function TAsn1EncodableVector.GetSelf(Index: Int32): IAsn1Encodable;
+begin
+  result := Flist[index];
+end;
+
+{ TAsn1Generator }
+
+constructor TAsn1Generator.Create(outStream: TStream);
+begin
+  F_out := outStream;
+end;
+
+function TAsn1Generator.GetOut: TStream;
+begin
+  result := F_out;
+end;
+
+{ TAsn1Null }
+
+function TAsn1Null.ToString: String;
+begin
+  result := 'NULL';
+end;
+
+{ TAsn1OctetString }
+
+function TAsn1OctetString.GetStr: TCryptoLibByteArray;
+begin
+  result := FStr;
+end;
+
+function TAsn1OctetString.GetParser: IAsn1OctetStringParser;
+begin
+  result := Self as IAsn1OctetStringParser;
+end;
+
+constructor TAsn1OctetString.Create(const Str: TCryptoLibByteArray);
+begin
+  Inherited Create();
+  if (Str = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SStrNil);
+  end;
+
+  FStr := Str;
+end;
+
+function TAsn1OctetString.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerOctetString;
+begin
+
+  if (not Supports(asn1Object, IDerOctetString, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := TArrayUtils.AreEqual(GetOctets(), other.GetOctets());
+end;
+
+function TAsn1OctetString.Asn1GetHashCode: Int32;
+begin
+  result := TArrayUtils.GetArrayHashCode(GetOctets());
+end;
+
+constructor TAsn1OctetString.Create(const obj: IAsn1Encodable);
+begin
+  Inherited Create();
+  try
+    FStr := obj.GetEncoded(TAsn1Encodable.Der);
+  except
+    on e: EIOCryptoLibException do
+    begin
+      raise EArgumentCryptoLibException.CreateResFmt(@SProcessingError,
+        [e.Message]);
+    end;
+  end;
+end;
+
+class function TAsn1OctetString.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IAsn1OctetString;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IAsn1OctetString))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := TBerOctetString.FromSequence
+    (TAsn1Sequence.GetInstance(o as TAsn1Object));
+end;
+
+class function TAsn1OctetString.GetInstance(const obj: TObject)
+  : IAsn1OctetString;
+var
+  asn1TaggedObject: IAsn1TaggedObject;
+begin
+  if ((obj = Nil) or (obj is TAsn1OctetString)) then
+  begin
+    result := obj as TAsn1OctetString;
+    Exit;
+  end;
+
+  // TODO: this needs to be deleted in V2
+  if Supports(obj, IAsn1TaggedObject, asn1TaggedObject) then
+  begin
+    result := GetInstance(asn1TaggedObject.GetObject() as TAsn1Object);
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+function TAsn1OctetString.GetOctets: TCryptoLibByteArray;
+begin
+  result := Str;
+end;
+
+function TAsn1OctetString.GetOctetStream: TStream;
+begin
+  // used TBytesStream here for one pass creation and population with byte array :)
+  result := TBytesStream.Create(Str);
+end;
+
+function TAsn1OctetString.ToString: String;
+begin
+  result := '#' + THex.Encode(Str);
+end;
+
+{ TAsn1Sequence }
+
+function TAsn1Sequence.GetCurrent(const e: IAsn1Encodable): IAsn1Encodable;
+var
+  encObj: IAsn1Encodable;
+begin
+  encObj := e;
+
+  // unfortunately null was allowed as a substitute for DER null
+  if (encObj = Nil) then
+  begin
+    result := TDerNull.Instance;
+    Exit;
+  end;
+
+  result := encObj;
+end;
+
+procedure TAsn1Sequence.AddObject(const obj: IAsn1Encodable);
+begin
+  FSeq.Add(obj);
+end;
+
+function TAsn1Sequence.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IAsn1Sequence;
+  l1, l2: TCryptoLibGenericArray<IAsn1Encodable>;
+  o1, o2: IAsn1Object;
+  idx: Int32;
+begin
+
+  if (not Supports(asn1Object, IAsn1Sequence, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  if (count <> other.count) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  l1 := GetEnumerable;
+  l2 := other.GetEnumerable;
+
+  for idx := System.Low(l1) to System.High(l1) do
+  begin
+    o1 := GetCurrent(l1[idx]).ToAsn1Object();
+    o2 := GetCurrent(l2[idx]).ToAsn1Object();
+
+    if (not(o1.Equals(o2))) then
+    begin
+      result := False;
+      Exit;
+    end;
+  end;
+
+  result := True;
+end;
+
+function TAsn1Sequence.Asn1GetHashCode: Int32;
+var
+  hc: Int32;
+  o: IAsn1Encodable;
+  LListAsn1Encodable: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  hc := count;
+
+  LListAsn1Encodable := Self.GetEnumerable;
+  for o in LListAsn1Encodable do
+  begin
+    hc := hc * 17;
+    if (o = Nil) then
+    begin
+      hc := hc xor TDerNull.Instance.GetHashCode();
+    end
+    else
+    begin
+      hc := hc xor o.GetHashCode();
+    end;
+  end;
+
+  result := hc;
+end;
+
+constructor TAsn1Sequence.Create(capacity: Int32);
+begin
+  inherited Create();
+  FSeq := TList<IAsn1Encodable>.Create();
+  FSeq.capacity := capacity;
+end;
+
+destructor TAsn1Sequence.Destroy;
+begin
+  FSeq.Free;
+  inherited Destroy;
+end;
+
+function TAsn1Sequence.GetCount: Int32;
+begin
+  result := FSeq.count;
+end;
+
+function TAsn1Sequence.GetEnumerable: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  result := FSeq.ToArray;
+end;
+
+class function TAsn1Sequence.GetInstance(const obj: TObject): IAsn1Sequence;
+var
+  primitive: IAsn1Object;
+  Sequence: IAsn1Sequence;
+  res: IAsn1SequenceParser;
+begin
+  if ((obj = Nil) or (obj is TAsn1Sequence)) then
+  begin
+    result := obj as TAsn1Sequence;
+    Exit;
+  end;
+
+  if (Supports(obj, IAsn1SequenceParser, res)) then
+  begin
+    result := TAsn1Sequence.GetInstance(res.ToAsn1Object() as TAsn1Object);
+    Exit;
+
+  end;
+
+  if (obj is TAsn1Encodable) then
+  begin
+    primitive := (obj as TAsn1Encodable).ToAsn1Object();
+
+    if (Supports(primitive, IAsn1Sequence, Sequence)) then
+    begin
+      result := Sequence;
+      Exit;
+    end;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SUnknownObject,
+    [obj.ClassName]);
+
+end;
+
+class function TAsn1Sequence.GetInstance(const obj: TCryptoLibByteArray)
+  : IAsn1Sequence;
+begin
+  try
+    result := TAsn1Sequence.GetInstance(FromByteArray(obj) as TAsn1Object);
+  except
+    on e: EIOCryptoLibException do
+    begin
+      raise EArgumentCryptoLibException.CreateResFmt(@SInvalidSequence,
+        [e.Message]);
+    end;
+  end;
+end;
+
+class function TAsn1Sequence.GetInstance(const obj: IAsn1TaggedObject;
+  explicitly: Boolean): IAsn1Sequence;
+var
+  inner: IAsn1Object;
+  Sequence: IAsn1Sequence;
+begin
+  inner := obj.GetObject();
+
+  if (explicitly) then
+  begin
+    if (not(obj.isExplicit())) then
+      raise EArgumentCryptoLibException.CreateRes(@SInvalidObject);
+
+    result := inner as IAsn1Sequence;
+    Exit;
+  end;
+
+  //
+  // constructed object which appears to be explicitly tagged
+  // when it should be implicit means we have to add the
+  // surrounding sequence.
+  //
+  if (obj.isExplicit()) then
+  begin
+    if (Supports(obj, IBerTaggedObject)) then
+    begin
+      result := TBerSequence.Create(inner);
+      Exit;
+    end;
+
+    result := TDerSequence.Create(inner);
+    Exit;
+  end;
+
+  if (Supports(inner, IAsn1Sequence, Sequence)) then
+  begin
+    result := Sequence;
+    Exit;
+  end;
+  raise EArgumentCryptoLibException.CreateResFmt(@SUnknownObject,
+    [(obj as TAsn1TaggedObject).ClassName]);
+
+end;
+
+function TAsn1Sequence.GetParser: IAsn1SequenceParser;
+begin
+  result := TAsn1SequenceParserImpl.Create(Self as IAsn1Sequence);
+end;
+
+function TAsn1Sequence.GetSelf(Index: Integer): IAsn1Encodable;
+begin
+  result := FSeq[index];
+end;
+
+function TAsn1Sequence.ToString: String;
+begin
+  result := TCollectionUtilities.ToStructuredString(FSeq);
+end;
+
+{ TAsn1Sequence.TAsn1SequenceParserImpl }
+
+constructor TAsn1Sequence.TAsn1SequenceParserImpl.Create
+  (const outer: IAsn1Sequence);
+begin
+  inherited Create();
+  Fouter := outer;
+  Fmax := outer.count;
+end;
+
+function TAsn1Sequence.TAsn1SequenceParserImpl.ReadObject: IAsn1Convertible;
+var
+  obj: IAsn1Encodable;
+  Sequence: IAsn1Sequence;
+  asn1Set: IAsn1Set;
+begin
+  if (Findex = Fmax) then
+  begin
+    result := Nil;
+    Exit;
+  end;
+
+  obj := Fouter[Findex];
+  System.Inc(Findex);
+
+  if (Supports(obj, IAsn1Sequence, Sequence)) then
+  begin
+    result := Sequence.parser;
+    Exit;
+  end;
+
+  if (Supports(obj, IAsn1Set, asn1Set)) then
+  begin
+    result := asn1Set.parser;
+    Exit;
+  end;
+
+  // NB: Asn1OctetString implements Asn1OctetStringParser directly
+  // if (obj is Asn1OctetString)
+  // return ((Asn1OctetString)obj).Parser;
+
+  result := obj;
+end;
+
+function TAsn1Sequence.TAsn1SequenceParserImpl.ToAsn1Object: IAsn1Object;
+begin
+  result := Fouter;
+end;
+
+{ TDerOctetString }
+
+constructor TDerOctetString.Create(const Str: TCryptoLibByteArray);
+begin
+  Inherited Create(Str);
+end;
+
+constructor TDerOctetString.Create(const obj: IAsn1Encodable);
+begin
+  Inherited Create(obj);
+end;
+
+destructor TDerOctetString.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TDerOctetString.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.OctetString, Str);
+end;
+
+class procedure TDerOctetString.Encode(const derOut: TDerOutputStream;
+  const bytes: TCryptoLibByteArray; offset, length: Int32);
+begin
+  derOut.WriteEncoded(TAsn1Tags.OctetString, bytes, offset, length);
+end;
+
+{ TBerOctetString }
+
+constructor TBerOctetString.Create(const octets: TList<IDerOctetString>);
+begin
+  Inherited Create(ToBytes(octets));
+  Focts := octets;
+end;
+
+constructor TBerOctetString.Create(const Str: TCryptoLibByteArray);
+begin
+  Inherited Create(Str);
+end;
+
+constructor TBerOctetString.Create(const obj: IAsn1Encodable);
+begin
+  Inherited Create(obj.ToAsn1Object());
+end;
+
+destructor TBerOctetString.Destroy;
+begin
+  Focts.Free;
+  inherited Destroy;
+end;
+
+constructor TBerOctetString.Create(const obj: IAsn1Object);
+begin
+  Inherited Create(obj);
+end;
+
+procedure TBerOctetString.Encode(const derOut: TStream);
+var
+  oct: IDerOctetString;
+  LListIDerOctetString: TCryptoLibGenericArray<IDerOctetString>;
+begin
+  if ((derOut is TAsn1OutputStream) or (derOut is TBerOutputStream)) then
+  begin
+    (derOut as TDerOutputStream).WriteByte(TAsn1Tags.Constructed or
+      TAsn1Tags.OctetString);
+
+    (derOut as TDerOutputStream).WriteByte($80);
+
+    //
+    // write out the octet array
+    //
+    LListIDerOctetString := Self.GetEnumerable;
+    for oct in LListIDerOctetString do
+    begin
+      (derOut as TDerOutputStream).WriteObject(oct);
+    end;
+
+    (derOut as TDerOutputStream).WriteByte($00);
+    (derOut as TDerOutputStream).WriteByte($00);
+  end
+  else
+  begin
+    (Inherited Encode(derOut));
+  end;
+end;
+
+class function TBerOctetString.FromSequence(const seq: IAsn1Sequence)
+  : IBerOctetString;
+var
+  v: TList<IDerOctetString>;
+  obj: IAsn1Encodable;
+  LListAsn1Encodable: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  v := TList<IDerOctetString>.Create();
+
+  LListAsn1Encodable := seq.GetEnumerable;
+  for obj in LListAsn1Encodable do
+  begin
+    v.Add(obj as IDerOctetString);
+  end;
+
+  result := TBerOctetString.Create(v);
+
+end;
+
+function TBerOctetString.GenerateOcts: TList<IDerOctetString>;
+var
+  I, endPoint: Int32;
+  nStr: TCryptoLibByteArray;
+begin
+  result := TList<IDerOctetString>.Create();
+  I := 0;
+  while I < System.length(Str) do
+  begin
+    endPoint := Min(System.length(Str), I + MaxLength);
+
+    System.SetLength(nStr, endPoint - I);
+
+    System.Move(Str[I], nStr[0], System.length(nStr) * System.SizeOf(Byte));
+    result.Add(TDerOctetString.Create(nStr) as IDerOctetString);
+    System.Inc(I, MaxLength);
+  end;
+end;
+
+function TBerOctetString.GetEnumerable: TCryptoLibGenericArray<IDerOctetString>;
+var
+  LList: TList<IDerOctetString>;
+begin
+
+  if (Focts = Nil) then
+  begin
+    LList := GenerateOcts();
+    try
+      result := LList.ToArray;
+      Exit;
+    finally
+      LList.Free;
+    end;
+  end;
+
+  result := Focts.ToArray;
+
+end;
+
+function TBerOctetString.GetOctets: TCryptoLibByteArray;
+begin
+  result := Str;
+end;
+
+class function TBerOctetString.ToBytes(octs: TList<IDerOctetString>)
+  : TCryptoLibByteArray;
+var
+  bOut: TMemoryStream;
+  o: IDerOctetString;
+  octets: TCryptoLibByteArray;
+begin
+  bOut := TMemoryStream.Create();
+  try
+    for o in octs do
+    begin
+      octets := o.GetOctets();
+      bOut.Write(octets[0], System.length(octets));
+    end;
+
+    System.SetLength(result, bOut.Size);
+    bOut.Position := 0;
+    bOut.Read(result[0], bOut.Size);
+  finally
+    bOut.Free;
+  end;
+end;
+
+{ TDerNull }
+
+function TDerNull.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+begin
+  result := Supports(asn1Object, IDerNull);
+end;
+
+function TDerNull.Asn1GetHashCode: Int32;
+begin
+  result := -1;
+end;
+
+{$IFNDEF _FIXINSIGHT_}
+
+constructor TDerNull.Create(dummy: Int32);
+begin
+  Inherited Create();
+end;
+{$ENDIF}
+
+procedure TDerNull.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.Null, ZeroBytes);
+end;
+
+class function TDerNull.GetInstance: IDerNull;
+begin
+  result := TDerNull.Create(0);
+end;
+
+{ TDerSequence }
+
+class function TDerSequence.GetEmpty: IDerSequence;
+begin
+  result := TDerSequence.Create();
+end;
+
+constructor TDerSequence.Create(const obj: IAsn1Encodable);
+begin
+  Inherited Create(1);
+  AddObject(obj);
+end;
+
+constructor TDerSequence.Create;
+begin
+  Inherited Create(0);
+end;
+
+constructor TDerSequence.Create(const v: IAsn1EncodableVector);
+var
+  ae: IAsn1Encodable;
+  LListAsn1Encodable: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  Inherited Create(v.count);
+  LListAsn1Encodable := v.GetEnumerable;
+  for ae in LListAsn1Encodable do
+  begin
+    AddObject(ae);
+  end;
+end;
+
+constructor TDerSequence.Create(const v: array of IAsn1Encodable);
+var
+  ae: IAsn1Encodable;
+begin
+  Inherited Create(System.length(v));
+  for ae in v do
+  begin
+    AddObject(ae);
+  end;
+end;
+
+destructor TDerSequence.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TDerSequence.Encode(const derOut: TStream);
+var
+  bOut: TMemoryStream;
+  dOut: TDerOutputStream;
+  obj: IAsn1Encodable;
+  bytes: TCryptoLibByteArray;
+  LListAsn1Encodable: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  // TODO Intermediate buffer could be avoided if we could calculate expected length
+  bOut := TMemoryStream.Create();
+  dOut := TDerOutputStream.Create(bOut);
+  try
+
+    LListAsn1Encodable := Self.GetEnumerable;
+    for obj in LListAsn1Encodable do
+    begin
+      dOut.WriteObject(obj);
+    end;
+
+    System.SetLength(bytes, bOut.Size);
+    bOut.Position := 0;
+    bOut.Read(bytes[0], bOut.Size);
+  finally
+    bOut.Free;
+    dOut.Free;
+  end;
+
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.Sequence or
+    TAsn1Tags.Constructed, bytes);
+end;
+
+class function TDerSequence.FromVector(const v: IAsn1EncodableVector)
+  : IDerSequence;
+begin
+  if v.count < 1 then
+  begin
+    result := Empty;
+  end
+  else
+  begin
+    result := TDerSequence.Create(v);
+  end;
+
+end;
+
+{ TBerSequence }
+
+class function TBerSequence.GetEmpty: IBerSequence;
+begin
+  result := TBerSequence.Create();
+end;
+
+constructor TBerSequence.Create(const obj: IAsn1Encodable);
+begin
+  Inherited Create(obj);
+end;
+
+constructor TBerSequence.Create;
+begin
+  Inherited Create();
+end;
+
+constructor TBerSequence.Create(const v: IAsn1EncodableVector);
+begin
+  Inherited Create(v);
+end;
+
+destructor TBerSequence.Destroy;
+begin
+  inherited Destroy;
+end;
+
+constructor TBerSequence.Create(const v: array of IAsn1Encodable);
+begin
+  Inherited Create(v);
+end;
+
+procedure TBerSequence.Encode(const derOut: TStream);
+var
+  o: IAsn1Encodable;
+  LListAsn1Encodable: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+
+  if ((derOut is TAsn1OutputStream) or (derOut is TBerOutputStream)) then
+  begin
+    (derOut as TDerOutputStream).WriteByte(TAsn1Tags.Sequence or
+      TAsn1Tags.Constructed);
+    (derOut as TDerOutputStream).WriteByte($80);
+
+    LListAsn1Encodable := Self.GetEnumerable;
+    for o in LListAsn1Encodable do
+    begin
+      (derOut as TDerOutputStream).WriteObject(o);
+    end;
+
+    (derOut as TDerOutputStream).WriteByte($00);
+    (derOut as TDerOutputStream).WriteByte($00);
+  end
+  else
+  begin
+    (Inherited Encode(derOut));
+  end;
+
+end;
+
+class function TBerSequence.FromVector(const v: IAsn1EncodableVector)
+  : IBerSequence;
+begin
+  if v.count < 1 then
+  begin
+    result := Empty;
+  end
+  else
+  begin
+    result := TBerSequence.Create(v);
+  end;
+
+end;
+
+{ TAsn1TaggedObject }
+
+function TAsn1TaggedObject.GetObject: IAsn1Object;
+begin
+  if (Fobj <> Nil) then
+  begin
+    result := Fobj.ToAsn1Object();
+    Exit;
+  end;
+
+  result := Nil;
+end;
+
+function TAsn1TaggedObject.GetTagNo: Int32;
+begin
+  result := FtagNo;
+end;
+
+function TAsn1TaggedObject.Getexplicitly: Boolean;
+begin
+  result := Fexplicitly;
+end;
+
+function TAsn1TaggedObject.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IAsn1TaggedObject;
+begin
+
+  if (not Supports(asn1Object, IAsn1TaggedObject, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := ((tagNo = other.tagNo) and
+    // TODO Should this be part of equality?
+    (explicitly = other.explicitly)) and
+    (GetObject().Equals(other.GetObject()));
+end;
+
+function TAsn1TaggedObject.Asn1GetHashCode: Int32;
+var
+  code: Int32;
+begin
+  code := Abs(tagNo);
+
+  // TODO: actually this is wrong - the problem is that a re-encoded
+  // object may end up with a different hashCode due to implicit
+  // tagging. As implicit tagging is ambiguous if a sequence is involved
+  // it seems the only correct method for both equals and hashCode is to
+  // compare the encodings...
+  // code := code xor explicitly.GetHashCode();
+
+  if (Fobj <> Nil) then
+  begin
+    code := code xor Fobj.GetHashCode();
+  end;
+
+  result := code;
+end;
+
+constructor TAsn1TaggedObject.Create(tagNo: Int32; const obj: IAsn1Encodable);
+begin
+  Inherited Create();
+  Fexplicitly := True;
+  FtagNo := tagNo;
+  Fobj := obj;
+end;
+
+constructor TAsn1TaggedObject.Create(explicitly: Boolean; tagNo: Int32;
+  const obj: IAsn1Encodable);
+begin
+  Inherited Create();
+  // IAsn1Choice marker interface 'insists' on explicit tagging
+  Fexplicitly := explicitly or (Supports(obj, IAsn1Choice));
+  FtagNo := tagNo;
+  Fobj := obj;
+end;
+
+class function TAsn1TaggedObject.GetInstance(obj: TObject): IAsn1TaggedObject;
+begin
+  if ((obj = Nil) or (obj is TAsn1TaggedObject)) then
+  begin
+    result := obj as TAsn1TaggedObject;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SUnknownObject,
+    [obj.ClassName]);
+end;
+
+function TAsn1TaggedObject.Getobj: IAsn1Encodable;
+begin
+  result := Fobj;
+end;
+
+class function TAsn1TaggedObject.GetInstance(const obj: IAsn1TaggedObject;
+  explicitly: Boolean): IAsn1TaggedObject;
+begin
+  if (explicitly) then
+  begin
+    result := obj.GetObject() as IAsn1TaggedObject;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateRes(@SImplicitObject);
+end;
+
+function TAsn1TaggedObject.GetObjectParser(tag: Int32; isExplicit: Boolean)
+  : IAsn1Convertible;
+begin
+  case tag of
+
+    TAsn1Tags.&Set:
+      begin
+        result := TAsn1Set.GetInstance(Self as IAsn1TaggedObject,
+          isExplicit).parser;
+        Exit;
+      end;
+    TAsn1Tags.Sequence:
+      begin
+        result := TAsn1Sequence.GetInstance(Self as IAsn1TaggedObject,
+          isExplicit).parser;
+        Exit;
+      end;
+    TAsn1Tags.OctetString:
+      begin
+        result := TAsn1OctetString.GetInstance(Self as IAsn1TaggedObject,
+          isExplicit).parser;
+        Exit;
+      end;
+  end;
+
+  if (isExplicit) then
+  begin
+    result := GetObject();
+    Exit;
+  end;
+
+  raise ENotImplementedCryptoLibException.CreateResFmt(@SImplicitTag, [tag]);
+
+end;
+
+class function TAsn1TaggedObject.IsConstructed(isExplicit: Boolean;
+  const obj: IAsn1Object): Boolean;
+var
+  Tagged: IAsn1TaggedObject;
+begin
+  if ((isExplicit) or (Supports(obj, IAsn1Sequence)) or
+    (Supports(obj, IAsn1Set))) then
+  begin
+    result := True;
+    Exit;
+  end;
+
+  if (not Supports(obj, IAsn1TaggedObject, Tagged)) then
+  begin
+    result := False;
+    Exit;
+  end;
+  result := IsConstructed(Tagged.isExplicit(), Tagged.GetObject());
+end;
+
+function TAsn1TaggedObject.IsEmpty: Boolean;
+begin
+  result := False; // empty;
+end;
+
+function TAsn1TaggedObject.isExplicit: Boolean;
+begin
+  result := Fexplicitly;
+end;
+
+function TAsn1TaggedObject.ToString: String;
+begin
+  result := '[' + IntToStr(tagNo) + ']' + (Fobj as TAsn1Encodable).ClassName;
+end;
+
+{ TAsn1Set }
+
+procedure TAsn1Set.AddObject(const obj: IAsn1Encodable);
+begin
+  F_set.Add(obj);
+end;
+
+function TAsn1Set.GetCurrent(const e: IAsn1Encodable): IAsn1Encodable;
+var
+  encObj: IAsn1Encodable;
+begin
+  encObj := e;
+
+  // unfortunately null was allowed as a substitute for DER null
+  if (encObj = Nil) then
+  begin
+    result := TDerNull.Instance;
+    Exit;
+  end;
+
+  result := encObj;
+end;
+
+function TAsn1Set.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IAsn1Set;
+  l1, l2: TCryptoLibGenericArray<IAsn1Encodable>;
+  o1, o2: IAsn1Object;
+  idx: Int32;
+begin
+
+  if (not Supports(asn1Object, IAsn1Set, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  if (count <> other.count) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  l1 := GetEnumerable;
+  l2 := other.GetEnumerable;
+
+  for idx := System.Low(l1) to System.High(l1) do
+  begin
+    o1 := GetCurrent(l1[idx]).ToAsn1Object();
+    o2 := GetCurrent(l2[idx]).ToAsn1Object();
+
+    if (not(o1.Equals(o2))) then
+    begin
+      result := False;
+      Exit;
+    end;
+  end;
+
+  result := True;
+end;
+
+function TAsn1Set.Asn1GetHashCode: Int32;
+var
+  hc: Int32;
+  o: IAsn1Encodable;
+  LListAsn1Encodable: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  hc := count;
+
+  LListAsn1Encodable := Self.GetEnumerable;
+  for o in LListAsn1Encodable do
+  begin
+    hc := hc * 17;
+    if (o = Nil) then
+    begin
+      hc := hc xor TDerNull.Instance.GetHashCode();
+    end
+    else
+    begin
+      hc := hc xor o.GetHashCode();
+    end;
+  end;
+
+  result := hc;
+end;
+
+constructor TAsn1Set.Create(capacity: Int32);
+begin
+  Inherited Create();
+  F_set := TList<IAsn1Encodable>.Create();
+  F_set.capacity := capacity;
+  FisSorted := False;
+end;
+
+destructor TAsn1Set.Destroy;
+begin
+  F_set.Free;
+  inherited Destroy;
+end;
+
+function TAsn1Set.GetCount: Int32;
+begin
+  result := F_set.count;
+end;
+
+function TAsn1Set.GetEnumerable: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  result := F_set.ToArray;
+end;
+
+class function TAsn1Set.GetInstance(const obj: TCryptoLibByteArray): IAsn1Set;
+begin
+  try
+    result := TAsn1Set.GetInstance(FromByteArray(obj) as TAsn1Object);
+  except
+    on e: EIOCryptoLibException do
+    begin
+      raise EArgumentCryptoLibException.CreateResFmt(@SInvalidSequence,
+        [e.Message]);
+    end;
+  end;
+end;
+
+class function TAsn1Set.GetInstance(const obj: IAsn1TaggedObject;
+  explicitly: Boolean): IAsn1Set;
+var
+  inner: IAsn1Object;
+  asn1Set: IAsn1Set;
+  asn1Sequence: IAsn1Sequence;
+  v: IAsn1EncodableVector;
+  ae: IAsn1Encodable;
+  LListAsn1Encodable: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  inner := obj.GetObject();
+
+  if (explicitly) then
+  begin
+    if (not(obj.isExplicit())) then
+      raise EArgumentCryptoLibException.CreateRes(@SInvalidObject);
+
+    result := inner as IAsn1Set;
+    Exit;
+  end;
+
+  //
+  // constructed object which appears to be explicitly tagged
+  // when it should be implicit means we have to add the
+  // surrounding sequence.
+  //
+  if (obj.isExplicit()) then
+  begin
+
+    result := TDerSet.Create(inner);
+    Exit;
+  end;
+
+  if (Supports(inner, IAsn1Set, asn1Set)) then
+  begin
+    result := asn1Set;
+    Exit;
+  end;
+  //
+  // in this case the parser returns a sequence, convert it
+  // into a set.
+  //
+
+  if (Supports(inner, IAsn1Sequence, asn1Sequence)) then
+  begin
+    v := TAsn1EncodableVector.Create();
+
+    LListAsn1Encodable := asn1Sequence.GetEnumerable;
+    for ae in LListAsn1Encodable do
+    begin
+      v.Add(ae);
+    end;
+
+    // TODO Should be able to construct set directly from sequence?
+    result := TDerSet.Create(v, False);
+    Exit;
+  end;
+  raise EArgumentCryptoLibException.CreateResFmt(@SUnknownObject,
+    [(obj as TAsn1TaggedObject).ClassName]);
+
+end;
+
+class function TAsn1Set.GetInstance(const obj: TObject): IAsn1Set;
+var
+  primitive: IAsn1Object;
+  asn1Set: IAsn1Set;
+  res: IAsn1SetParser;
+begin
+  if ((obj = Nil) or (obj is TAsn1Set)) then
+  begin
+    result := obj as TAsn1Set;
+    Exit;
+  end;
+
+  if (Supports(obj, IAsn1SetParser, res)) then
+  begin
+    result := TAsn1Set.GetInstance(res.ToAsn1Object() as TAsn1Object);
+    Exit;
+
+  end;
+
+  if (obj is TAsn1Encodable) then
+  begin
+    primitive := (obj as TAsn1Encodable).ToAsn1Object();
+
+    if (Supports(primitive, IAsn1Set, asn1Set)) then
+    begin
+      result := asn1Set;
+      Exit;
+    end;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SUnknownObject,
+    [obj.ClassName]);
+
+end;
+
+function TAsn1Set.GetParser: IAsn1SetParser;
+begin
+  result := TAsn1SetParserImpl.Create(Self as IAsn1Set);
+end;
+
+function TAsn1Set.GetSelf(Index: Integer): IAsn1Encodable;
+begin
+  result := F_set[index];
+end;
+
+function TAsn1Set.LessThanOrEqual(const a, b: TCryptoLibByteArray): Boolean;
+var
+  len, I: Int32;
+begin
+  len := Math.Min(System.length(a), System.length(b));
+
+  I := 0;
+  while I <> len do
+  begin
+
+    if (a[I] <> b[I]) then
+    begin
+
+      result := (a[I]) < (b[I]);
+      Exit;
+    end;
+    System.Inc(I);
+  end;
+
+  result := len = System.length(a);
+end;
+
+procedure TAsn1Set.Sort;
+var
+  swapped: Boolean;
+  lastSwap, Index, swapIndex: Int32;
+  a, b: TCryptoLibByteArray;
+  temp: IAsn1Encodable;
+
+begin
+  if (not FisSorted) then
+  begin
+    FisSorted := True;
+    if (F_set.count > 1) then
+    begin
+      swapped := True;
+      lastSwap := F_set.count - 1;
+
+      while (swapped) do
+      begin
+        index := 0;
+        swapIndex := 0;
+        a := F_set[0].GetEncoded(TAsn1Encodable.Der);
+
+        swapped := False;
+
+        while (index <> lastSwap) do
+        begin
+          b := F_set[index + 1].GetEncoded(TAsn1Encodable.Der);
+
+          if (LessThanOrEqual(a, b)) then
+          begin
+            a := b;
+          end
+          else
+          begin
+            temp := F_set[index];
+            // Review being picky for copy
+            // temp := System.Copy(F_set.List, Index, 1)[0];
+            F_set[index] := F_set[index + 1];
+            F_set[index + 1] := temp;
+
+            swapped := True;
+            swapIndex := index;
+          end;
+
+          System.Inc(index);
+        end;
+
+        lastSwap := swapIndex;
+      end;
+    end;
+  end;
+end;
+
+function TAsn1Set.ToArray: TCryptoLibGenericArray<IAsn1Encodable>;
+var
+  values: TCryptoLibGenericArray<IAsn1Encodable>;
+  I: Int32;
+begin
+  System.SetLength(values, count);
+  for I := 0 to System.Pred(count) do
+  begin
+    values[I] := Self[I];
+  end;
+
+  result := values;
+end;
+
+function TAsn1Set.ToString: String;
+begin
+  result := TCollectionUtilities.ToStructuredString(F_set);
+end;
+
+{ TAsn1Set.TAsn1SetParserImpl }
+
+constructor TAsn1Set.TAsn1SetParserImpl.Create(const outer: IAsn1Set);
+begin
+  Inherited Create();
+  Fouter := outer;
+  Fmax := outer.count;
+end;
+
+function TAsn1Set.TAsn1SetParserImpl.ReadObject: IAsn1Convertible;
+var
+  obj: IAsn1Encodable;
+  Sequence: IAsn1Sequence;
+  asn1Set: IAsn1Set;
+begin
+  if (Findex = Fmax) then
+  begin
+    result := Nil;
+    Exit;
+  end;
+
+  obj := Fouter[Findex];
+  System.Inc(Findex);
+
+  if (Supports(obj, IAsn1Sequence, Sequence)) then
+  begin
+    result := Sequence.parser;
+    Exit;
+  end;
+
+  if (Supports(obj, IAsn1Set, asn1Set)) then
+  begin
+    result := asn1Set.parser;
+    Exit;
+  end;
+
+  // NB: Asn1OctetString implements Asn1OctetStringParser directly
+  // if (obj is Asn1OctetString)
+  // return ((Asn1OctetString)obj).Parser;
+
+  result := obj;
+end;
+
+function TAsn1Set.TAsn1SetParserImpl.ToAsn1Object: IAsn1Object;
+begin
+  result := Fouter;
+end;
+
+{ TDerSet }
+
+class function TDerSet.GetEmpty: IDerSet;
+begin
+  result := TDerSet.Create();
+end;
+
+constructor TDerSet.Create(const v: array of IAsn1Encodable);
+var
+  o: IAsn1Encodable;
+begin
+  Inherited Create(System.length(v));
+  for o in v do
+  begin
+    AddObject(o);
+  end;
+
+  Sort();
+end;
+
+constructor TDerSet.Create;
+begin
+  Inherited Create(0);
+end;
+
+constructor TDerSet.Create(const v: IAsn1EncodableVector;
+  needsSorting: Boolean);
+var
+  o: IAsn1Encodable;
+  LListAsn1Encodable: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  Inherited Create(v.count);
+  LListAsn1Encodable := v.GetEnumerable;
+  for o in LListAsn1Encodable do
+  begin
+    AddObject(o);
+  end;
+
+  if (needsSorting) then
+  begin
+    Sort();
+  end;
+end;
+
+constructor TDerSet.Create(const obj: IAsn1Encodable);
+begin
+  Inherited Create(1);
+  AddObject(obj);
+end;
+
+constructor TDerSet.Create(const v: IAsn1EncodableVector);
+begin
+  Create(v, True);
+end;
+
+destructor TDerSet.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TDerSet.Encode(const derOut: TStream);
+var
+  bOut: TMemoryStream;
+  dOut: TDerOutputStream;
+  obj: IAsn1Encodable;
+  bytes: TCryptoLibByteArray;
+  LListAsn1Encodable: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  // TODO Intermediate buffer could be avoided if we could calculate expected length
+  bOut := TMemoryStream.Create();
+  dOut := TDerOutputStream.Create(bOut);
+
+  try
+    LListAsn1Encodable := Self.GetEnumerable;
+    for obj in LListAsn1Encodable do
+    begin
+      dOut.WriteObject(obj);
+    end;
+
+    System.SetLength(bytes, bOut.Size);
+    bOut.Position := 0;
+    bOut.Read(bytes[0], bOut.Size);
+  finally
+    bOut.Free;
+    dOut.Free;
+  end;
+
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.&Set or
+    TAsn1Tags.Constructed, bytes);
+end;
+
+class function TDerSet.FromVector(const v: IAsn1EncodableVector;
+  needsSorting: Boolean): IDerSet;
+begin
+  if v.count < 1 then
+  begin
+    result := Empty;
+  end
+  else
+  begin
+    result := TDerSet.Create(v, needsSorting);
+  end;
+end;
+
+class function TDerSet.FromVector(const v: IAsn1EncodableVector): IDerSet;
+begin
+  if v.count < 1 then
+  begin
+    result := Empty;
+  end
+  else
+  begin
+    result := TDerSet.Create(v);
+  end;
+end;
+
+{ TAsn1StreamParser }
+
+procedure TAsn1StreamParser.Set00Check(enabled: Boolean);
+var
+  indefiniteLengthInputStream: TIndefiniteLengthInputStream;
+begin
+  if (F_in is TIndefiniteLengthInputStream) then
+  begin
+    indefiniteLengthInputStream := F_in as TIndefiniteLengthInputStream;
+    indefiniteLengthInputStream.SetEofOn00(enabled);
+  end;
+end;
+
+constructor TAsn1StreamParser.Create(const inStream: TStream);
+begin
+  Create(inStream, TAsn1InputStream.FindLimit(inStream));
+end;
+
+constructor TAsn1StreamParser.Create(const inStream: TStream; limit: Int32);
+begin
+  Inherited Create();
+  F_in := inStream;
+  F_limit := limit;
+  System.SetLength(FtmpBuffers, 16);
+end;
+
+constructor TAsn1StreamParser.Create(const encoding: TCryptoLibByteArray);
+begin
+  // used TBytesStream here for one pass creation and population with byte array :)
+  Create(TBytesStream.Create(encoding), System.length(encoding));
+
+end;
+
+destructor TAsn1StreamParser.Destroy;
+begin
+  F_in.Free;
+  inherited Destroy;
+end;
+
+function TAsn1StreamParser.ReadVector: IAsn1EncodableVector;
+var
+  v: IAsn1EncodableVector;
+  obj: IAsn1Convertible;
+begin
+  v := TAsn1EncodableVector.Create();
+
+  obj := ReadObject();
+  while (obj <> Nil) do
+  begin
+    v.Add([obj.ToAsn1Object()]);
+    obj := ReadObject();
+  end;
+
+  result := v;
+end;
+
+function TAsn1StreamParser.ReadImplicit(Constructed: Boolean; tag: Int32)
+  : IAsn1Convertible;
+begin
+  if (F_in is TIndefiniteLengthInputStream) then
+  begin
+    if (not Constructed) then
+    begin
+      raise EIOCryptoLibException.CreateRes(@SIndefiniteLength);
+    end;
+
+    result := ReadIndef(tag);
+    Exit;
+  end;
+
+  if (Constructed) then
+  begin
+    case tag of
+
+      TAsn1Tags.&Set:
+        begin
+          result := TDerSetParser.Create(Self as IAsn1StreamParser);
+          Exit;
+        end;
+      TAsn1Tags.Sequence:
+        begin
+          result := TDerSequenceParser.Create(Self as IAsn1StreamParser);
+          Exit;
+        end;
+      TAsn1Tags.OctetString:
+        begin
+          result := TBerOctetStringParser.Create(Self as IAsn1StreamParser);
+          Exit;
+        end;
+
+    end;
+  end
+  else
+  begin
+    case tag of
+
+      TAsn1Tags.&Set:
+        begin
+          raise EAsn1CryptoLibException.CreateRes(@SUnConstructedEncoding);
+        end;
+      TAsn1Tags.Sequence:
+        begin
+          raise EAsn1CryptoLibException.CreateRes(@SUnConstructedEncoding2);
+        end;
+      TAsn1Tags.OctetString:
+        begin
+          result := TDerOctetStringParser.Create
+            (F_in as TDefiniteLengthInputStream);
+          Exit;
+        end;
+    end;
+
+  end;
+
+  raise EAsn1CryptoLibException.CreateRes(@SImplicitTagging);
+
+end;
+
+function TAsn1StreamParser.ReadIndef(tagValue: Int32): IAsn1Convertible;
+begin
+  // Note: INDEF => CONSTRUCTED
+
+  // TODO There are other tags that may be constructed (e.g. BIT_STRING)
+  case tagValue of
+    TAsn1Tags.External:
+      begin
+        result := TDerExternalParser.Create(Self as IAsn1StreamParser);
+        Exit;
+      end;
+
+    TAsn1Tags.OctetString:
+      begin
+        result := TBerOctetStringParser.Create(Self as IAsn1StreamParser);
+        Exit;
+      end;
+
+    TAsn1Tags.Sequence:
+      begin
+        result := TBerSequenceParser.Create(Self as IAsn1StreamParser);
+        Exit;
+      end;
+
+    TAsn1Tags.&Set:
+      begin
+        result := TBerSetParser.Create(Self as IAsn1StreamParser);
+        Exit;
+      end;
+
+  else
+    begin
+      raise EAsn1CryptoLibException.CreateResFmt(@SUnknownObjectBER,
+        [tagValue]);
+    end;
+
+  end;
+end;
+
+function TAsn1StreamParser.ReadObject: IAsn1Convertible;
+var
+  tag, tagNo, &length: Int32;
+  IsConstructed: Boolean;
+  indIn: TIndefiniteLengthInputStream;
+  sp: IAsn1StreamParser;
+  defIn: TDefiniteLengthInputStream;
+begin
+  tag := TStreamSorter.ReadByte(F_in);
+
+  if (tag = -1) then
+  begin
+    result := Nil;
+    Exit;
+  end;
+
+  // turn off looking for "00" while we resolve the tag
+  Set00Check(False);
+
+  //
+  // calculate tag number
+  //
+  tagNo := TAsn1InputStream.ReadTagNumber(F_in, tag);
+
+  IsConstructed := (tag and TAsn1Tags.Constructed) <> 0;
+
+  //
+  // calculate length
+  //
+  length := TAsn1InputStream.ReadLength(F_in, F_limit);
+
+  if (length < 0) then // indefinite length method
+  begin
+    if (not IsConstructed) then
+    begin
+      raise EIOCryptoLibException.CreateRes(@SIndefiniteLength);
+    end;
+
+    indIn := TIndefiniteLengthInputStream.Create(F_in, F_limit);
+
+    sp := TAsn1StreamParser.Create(indIn, F_limit);
+
+    if ((tag and TAsn1Tags.Application) <> 0) then
+    begin
+
+      result := TBerApplicationSpecificParser.Create(tagNo, sp);
+      Exit;
+
+    end;
+
+    if ((tag and TAsn1Tags.Tagged) <> 0) then
+    begin
+
+      result := TBerTaggedObjectParser.Create(True, tagNo, sp);
+      Exit;
+
+    end;
+
+    result := sp.ReadIndef(tagNo);
+    Exit;
+
+  end;
+
+  defIn := TDefiniteLengthInputStream.Create(F_in, length);
+
+  if ((tag and TAsn1Tags.Application) <> 0) then
+  begin
+    try
+      result := TDerApplicationSpecific.Create(IsConstructed, tagNo,
+        defIn.ToArray());
+      Exit;
+    finally
+      defIn.Free;
+    end;
+  end;
+
+  if ((tag and TAsn1Tags.Tagged) <> 0) then
+  begin
+    result := TBerTaggedObjectParser.Create(IsConstructed, tagNo,
+      TAsn1StreamParser.Create(defIn) as IAsn1StreamParser);
+    Exit;
+
+  end;
+
+  if (IsConstructed) then
+  begin
+    // TODO There are other tags that may be constructed (e.g. BitString)
+    case tagNo of
+
+      TAsn1Tags.OctetString:
+        begin
+          //
+          // yes, people actually do this...
+          //
+
+          result := TBerOctetStringParser.Create(TAsn1StreamParser.Create(defIn)
+            as IAsn1StreamParser);
+          Exit;
+
+        end;
+      TAsn1Tags.Sequence:
+        begin
+
+          result := TDerSequenceParser.Create(TAsn1StreamParser.Create(defIn)
+            as IAsn1StreamParser);
+          Exit;
+
+        end;
+      TAsn1Tags.&Set:
+        begin
+
+          result := TDerSetParser.Create(TAsn1StreamParser.Create(defIn)
+            as IAsn1StreamParser);
+          Exit;
+
+        end;
+
+      TAsn1Tags.External:
+        begin
+
+          result := TDerExternalParser.Create(TAsn1StreamParser.Create(defIn)
+            as IAsn1StreamParser);
+          Exit;
+
+        end;
+    else
+      begin
+        defIn.Free; // free the stream incase an unsupported tag is encountered.
+        raise EIOCryptoLibException.CreateResFmt(@SUnknownTag, [tagNo]);
+      end;
+
+    end;
+  end;
+
+  // Some primitive encodings can be handled by parsers too...
+  case tagNo of
+    TAsn1Tags.OctetString:
+      begin
+        result := TDerOctetStringParser.Create(defIn);
+        Exit;
+      end;
+  end;
+
+  try
+    try
+      result := TAsn1InputStream.CreatePrimitiveDerObject(tagNo, defIn,
+        FtmpBuffers);
+      Exit;
+
+    except
+
+      on e: EArgumentCryptoLibException do
+      begin
+        raise EAsn1CryptoLibException.CreateResFmt(@SCorruptedStream,
+          [e.Message]);
+      end;
+
+    end;
+  finally
+    defIn.Free;
+  end;
+
+end;
+
+function TAsn1StreamParser.ReadTaggedObject(Constructed: Boolean; tag: Int32)
+  : IAsn1Object;
+var
+  defIn: TDefiniteLengthInputStream;
+  v: IAsn1EncodableVector;
+begin
+  if (not Constructed) then
+  begin
+    // Note: !CONSTRUCTED => IMPLICIT
+    defIn := F_in as TDefiniteLengthInputStream;
+    result := TDerTaggedObject.Create(False, tag,
+      TDerOctetString.Create(defIn.ToArray()));
+    Exit;
+  end;
+
+  v := ReadVector();
+
+  if (F_in is TIndefiniteLengthInputStream) then
+  begin
+    if v.count = 1 then
+    begin
+      result := TBerTaggedObject.Create(True, tag, v[0]);
+      Exit;
+    end
+    else
+    begin
+      result := TBerTaggedObject.Create(False, tag, TBerSequence.FromVector(v));
+      Exit;
+    end;
+
+  end;
+
+  if v.count = 1 then
+  begin
+    result := TDerTaggedObject.Create(True, tag, v[0]);
+    Exit;
+  end
+  else
+  begin
+    result := TDerTaggedObject.Create(False, tag, TDerSequence.FromVector(v));
+    Exit;
+  end;
+
+end;
+
+{ TDerSetParser }
+
+constructor TDerSetParser.Create(const parser: IAsn1StreamParser);
+begin
+  F_parser := parser;
+end;
+
+function TDerSetParser.ReadObject: IAsn1Convertible;
+begin
+  result := F_parser.ReadObject();
+end;
+
+function TDerSetParser.ToAsn1Object: IAsn1Object;
+begin
+  result := TDerSet.Create(F_parser.ReadVector(), False);
+end;
+
+{ TDerSequenceParser }
+
+constructor TDerSequenceParser.Create(const parser: IAsn1StreamParser);
+begin
+  F_parser := parser;
+end;
+
+function TDerSequenceParser.ReadObject: IAsn1Convertible;
+begin
+  result := F_parser.ReadObject();
+end;
+
+function TDerSequenceParser.ToAsn1Object: IAsn1Object;
+begin
+  result := TDerSequence.Create(F_parser.ReadVector());
+end;
+
+{ TDerApplicationSpecific }
+
+function TDerApplicationSpecific.GetApplicationTag: Int32;
+begin
+  result := Ftag;
+end;
+
+function TDerApplicationSpecific.GetContents: TCryptoLibByteArray;
+begin
+  result := Foctets;
+end;
+
+function TDerApplicationSpecific.IsConstructed: Boolean;
+begin
+  result := FisConstructed;
+end;
+
+function TDerApplicationSpecific.GetLengthOfHeader
+  (const data: TCryptoLibByteArray): Int32;
+var
+  &length, Size: Int32;
+begin
+  length := data[1]; // TODO: assumes 1 byte tag
+
+  if (length = $80) then
+  begin
+    result := 2; // indefinite-length encoding
+    Exit;
+  end;
+
+  if (length > 127) then
+  begin
+    Size := length and $7F;
+
+    // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
+    if (Size > 4) then
+    begin
+      raise EInvalidOperationCryptoLibException.CreateResFmt
+        (@SInvalidDerLength, [Size]);
+    end;
+
+    result := Size + 2;
+    Exit;
+  end;
+
+  result := 2;
+end;
+
+constructor TDerApplicationSpecific.Create(tag: Int32;
+  const obj: IAsn1Encodable);
+begin
+  Create(True, tag, obj);
+end;
+
+constructor TDerApplicationSpecific.Create(tag: Int32;
+  const octets: TCryptoLibByteArray);
+begin
+  Create(False, tag, octets);
+end;
+
+constructor TDerApplicationSpecific.Create(IsConstructed: Boolean; tag: Int32;
+  const octets: TCryptoLibByteArray);
+begin
+  Inherited Create();
+  FisConstructed := IsConstructed;
+  Ftag := tag;
+  Foctets := octets;
+end;
+
+function TDerApplicationSpecific.Asn1Equals(const asn1Object
+  : IAsn1Object): Boolean;
+var
+  other: IDerApplicationSpecific;
+begin
+
+  if (not Supports(asn1Object, IDerApplicationSpecific, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := (IsConstructed = other.IsConstructed) and
+    (ApplicationTag = other.ApplicationTag) and
+    TArrayUtils.AreEqual(GetContents, other.GetContents);
+end;
+
+function TDerApplicationSpecific.Asn1GetHashCode: Int32;
+var
+  HashCode: Int32;
+begin
+  case IsConstructed of
+    True:
+      HashCode := 1;
+    False:
+      HashCode := 0;
+  end;
+  result := HashCode xor Ftag xor TArrayUtils.GetArrayHashCode(Foctets);
+end;
+
+constructor TDerApplicationSpecific.Create(tagNo: Int32;
+  const vec: IAsn1EncodableVector);
+var
+  bOut: TMemoryStream;
+  bs: TCryptoLibByteArray;
+  I: Int32;
+  val: IAsn1Encodable;
+begin
+  Inherited Create();
+  Ftag := tagNo;
+  FisConstructed := True;
+
+  bOut := TMemoryStream.Create();
+  try
+    I := 0;
+    while I <> vec.count do
+
+    begin
+      try
+        val := vec[I];
+        bs := val.GetDerEncoded();
+        bOut.Write(bs[0], System.length(bs));
+      except
+        on e: EIOCryptoLibException do
+        begin
+          raise EInvalidOperationCryptoLibException.CreateResFmt
+            (@SMalformedObject, [e.Message]);
+        end;
+      end;
+      System.Inc(I);
+    end;
+
+    System.SetLength(Foctets, bOut.Size);
+    bOut.Position := 0;
+    bOut.Read(Foctets[0], bOut.Size);
+
+  finally
+    bOut.Free;
+  end;
+
+end;
+
+procedure TDerApplicationSpecific.Encode(const derOut: TStream);
+var
+  classBits: Int32;
+begin
+  classBits := TAsn1Tags.Application;
+  if (IsConstructed) then
+  begin
+    classBits := classBits or TAsn1Tags.Constructed;
+  end;
+
+  (derOut as TDerOutputStream).WriteEncoded(classBits, Ftag, Foctets);
+end;
+
+constructor TDerApplicationSpecific.Create(isExplicit: Boolean; tag: Int32;
+  const obj: IAsn1Encodable);
+var
+  asn1Obj: IAsn1Object;
+  data, tmp: TCryptoLibByteArray;
+  lenBytes: Int32;
+begin
+  Inherited Create();
+  asn1Obj := obj.ToAsn1Object();
+
+  data := asn1Obj.GetDerEncoded();
+
+  FisConstructed := TAsn1TaggedObject.IsConstructed(isExplicit, asn1Obj);
+  Ftag := tag;
+
+  if (isExplicit) then
+  begin
+    Foctets := data;
+  end
+  else
+  begin
+    lenBytes := GetLengthOfHeader(data);
+    System.SetLength(tmp, System.length(data) - lenBytes);
+    System.Move(data[lenBytes], tmp[0], System.length(tmp) *
+      System.SizeOf(Byte));
+    Foctets := tmp;
+  end;
+end;
+
+function TDerApplicationSpecific.GetObject: IAsn1Object;
+begin
+  result := FromByteArray(GetContents());
+end;
+
+function TDerApplicationSpecific.GetObject(derTagNo: Int32): IAsn1Object;
+var
+  orig, tmp: TCryptoLibByteArray;
+begin
+  if (derTagNo >= $1F) then
+  begin
+    raise EIOCryptoLibException.CreateRes(@SUnSupportedTag);
+  end;
+
+  orig := GetEncoded();
+  tmp := ReplaceTagNumber(derTagNo, orig);
+
+  if ((orig[0] and TAsn1Tags.Constructed) <> 0) then
+  begin
+    tmp[0] := tmp[0] or TAsn1Tags.Constructed;
+  end;
+
+  result := FromByteArray(tmp);
+end;
+
+class function TDerApplicationSpecific.ReplaceTagNumber(newTag: Int32;
+  const input: TCryptoLibByteArray): TCryptoLibByteArray;
+var
+  tagNo, Index, b, Remaining: Int32;
+  tmp: TCryptoLibByteArray;
+begin
+  tagNo := input[0] and $1F;
+  index := 1;
+  //
+  // with tagged object tag number is bottom 5 bits, or stored at the start of the content
+  //
+  if (tagNo = $1F) then
+  begin
+
+    b := input[index];
+    System.Inc(index);
+
+    // X.690-0207 8.1.2.4.2
+    // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
+    if ((b and $7F) = 0) then // Note: -1 will pass
+    begin
+      raise EIOCryptoLibException.CreateRes(@SCorruptedStreamInvalidTag);
+    end;
+
+    while ((b and $80) <> 0) do
+    begin
+      b := input[index];
+      System.Inc(index);
+    end;
+
+  end;
+
+  Remaining := System.length(input) - index;
+  System.SetLength(tmp, 1 + Remaining);
+  tmp[0] := Byte(newTag);
+  System.Move(input[index], tmp[1], Remaining * System.SizeOf(Byte));
+
+  result := tmp;
+end;
+
+{ TBerApplicationSpecific }
+
+constructor TBerApplicationSpecific.Create(tagNo: Int32;
+  const vec: IAsn1EncodableVector);
+begin
+  inherited Create(tagNo, vec);
+end;
+
+{ TBerOctetStringParser }
+
+constructor TBerOctetStringParser.Create(const parser: IAsn1StreamParser);
+begin
+  Inherited Create();
+  F_parser := parser;
+end;
+
+function TBerOctetStringParser.GetOctetStream: TStream;
+begin
+  result := TConstructedOctetStream.Create(F_parser);
+end;
+
+function TBerOctetStringParser.ToAsn1Object: IAsn1Object;
+var
+  LStream: TStream;
+begin
+  try
+    LStream := GetOctetStream();
+
+    try
+      result := TBerOctetString.Create(TStreamUtils.ReadAll(LStream));
+    finally
+      LStream.Free;
+    end;
+
+  except
+    on e: EIOCryptoLibException do
+    begin
+      raise EAsn1ParsingCryptoLibException.CreateResFmt(@SConvertError,
+        [e.Message]);
+    end;
+
+  end;
+end;
+
+{ TBerApplicationSpecificParser }
+
+constructor TBerApplicationSpecificParser.Create(tag: Int32;
+  const parser: IAsn1StreamParser);
+begin
+  F_tag := tag;
+  F_parser := parser;
+end;
+
+function TBerApplicationSpecificParser.ReadObject: IAsn1Convertible;
+begin
+  result := F_parser.ReadObject();
+end;
+
+function TBerApplicationSpecificParser.ToAsn1Object: IAsn1Object;
+begin
+  result := TBerApplicationSpecific.Create(F_tag, F_parser.ReadVector());
+end;
+
+{ TDerStringBase }
+
+function TDerStringBase.Asn1GetHashCode: Int32;
+begin
+  result := TStringUtils.GetStringHashCode(GetString());
+end;
+
+constructor TDerStringBase.Create;
+begin
+  Inherited Create();
+end;
+
+function TDerStringBase.ToString: String;
+begin
+  result := GetString();
+end;
+
+{ TDerBitString }
+
+class function TDerBitString.GetInstance(const obj: TCryptoLibByteArray)
+  : IDerBitString;
+begin
+  try
+    result := FromByteArray(obj) as IDerBitString;
+  except
+    on e: Exception do
+    begin
+      raise EArgumentCryptoLibException.CreateResFmt(@SEncodingError,
+        [e.Message]);
+    end;
+
+  end;
+end;
+
+function TDerBitString.GetmData: TCryptoLibByteArray;
+begin
+  result := FmData;
+end;
+
+function TDerBitString.GetmPadBits: Int32;
+begin
+  result := FmPadBits;
+end;
+
+function TDerBitString.GetOctets: TCryptoLibByteArray;
+begin
+  if (mPadBits <> 0) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes(@SUnalignedData);
+  end;
+  result := System.Copy(mData);
+end;
+
+function TDerBitString.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerBitString;
+begin
+
+  if (not Supports(asn1Object, IDerBitString, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := (mPadBits = other.mPadBits) and
+    (TArrayUtils.AreEqual(mData, other.mData));
+end;
+
+constructor TDerBitString.Create(const data: TCryptoLibByteArray;
+  padBits: Int32);
+begin
+  Inherited Create();
+  if (data = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SDataNil);
+  end;
+
+  if ((padBits < 0) or (padBits > 7)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidRange);
+  end;
+
+  if ((System.length(data) = 0) and (padBits <> 0)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SPadBitError);
+  end;
+
+  FmData := System.Copy(data);
+  FmPadBits := padBits;
+
+end;
+
+constructor TDerBitString.Create(const data: TCryptoLibByteArray);
+begin
+  Create(data, 0);
+end;
+
+constructor TDerBitString.Create(namedBits: Int32);
+var
+  bits, bytes, I, padBits: Int32;
+  data: TCryptoLibByteArray;
+begin
+  Inherited Create();
+  if (namedBits = 0) then
+  begin
+    System.SetLength(FmData, 0);
+    FmPadBits := 0;
+    Exit;
+  end;
+  bits := TBigInteger.BitLen(namedBits);
+  bytes := (bits + 7) div 8;
+
+{$IFDEF DEBUG}
+  System.Assert((0 < bytes) and (bytes <= 4));
+{$ENDIF DEBUG}
+  System.SetLength(data, bytes);
+
+  System.Dec(bytes);
+
+  for I := 0 to System.Pred(bytes) do
+  begin
+    data[I] := Byte(namedBits);
+    namedBits := TBits.Asr32(namedBits, 8);
+  end;
+
+{$IFDEF DEBUG}
+  System.Assert((namedBits and $FF) <> 0);
+{$ENDIF DEBUG}
+  data[bytes] := Byte(namedBits);
+
+  padBits := 0;
+  while ((namedBits and (1 shl padBits)) = 0) do
+  begin
+    System.Inc(padBits);
+  end;
+
+{$IFDEF DEBUG}
+  System.Assert(padBits < 8);
+{$ENDIF DEBUG}
+  FmData := data;
+  FmPadBits := padBits;
+end;
+
+procedure TDerBitString.Encode(const derOut: TStream);
+var
+  last, mask, unusedBits: Int32;
+  contents: TCryptoLibByteArray;
+begin
+  if (mPadBits > 0) then
+  begin
+    last := mData[System.length(mData) - 1];
+    mask := (1 shl mPadBits) - 1;
+    unusedBits := last and mask;
+
+    if (unusedBits <> 0) then
+    begin
+      contents := TArrayUtils.Prepend(mData, Byte(mPadBits));
+
+      // /*
+      // * X.690-0207 11.2.1: Each unused bit in the final octet of the encoding of a bit string value shall be set to zero.
+      // */
+      contents[System.length(contents) - 1] := Byte(last xor unusedBits);
+
+      (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.BitString, contents);
+      Exit;
+    end;
+  end;
+
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.BitString,
+    Byte(mPadBits), mData);
+end;
+
+class function TDerBitString.FromAsn1Octets(const octets: TCryptoLibByteArray)
+  : IDerBitString;
+var
+  padBits, last, mask: Int32;
+  data: TCryptoLibByteArray;
+begin
+  if (System.length(octets) < 1) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@STruncatedBitString);
+  end;
+
+  padBits := octets[0];
+  data := TArrayUtils.CopyOfRange(octets, 1, System.length(octets));
+
+  if ((padBits > 0) and (padBits < 8) and (System.length(data) > 0)) then
+  begin
+    last := data[System.length(data) - 1];
+    mask := (1 shl padBits) - 1;
+
+    if ((last and mask) <> 0) then
+    begin
+      result := TBerBitString.Create(data, padBits);
+      Exit;
+    end;
+  end;
+
+  result := TDerBitString.Create(data, padBits);
+end;
+
+function TDerBitString.GetBytes: TCryptoLibByteArray;
+begin
+  result := System.Copy(mData);
+
+  // DER requires pad bits be zero
+  if (mPadBits > 0) then
+  begin
+    result[System.length(result) - 1] := result[System.length(result) - 1] and
+      Byte($FF shl mPadBits);
+  end;
+
+end;
+
+class function TDerBitString.GetInstance(const obj: TObject): IDerBitString;
+begin
+  if ((obj = Nil) or (obj is TDerBitString)) then
+  begin
+    result := obj as TDerBitString;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerBitString.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerBitString;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerBitString))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := FromAsn1Octets((o as IAsn1OctetString).GetOctets());
+end;
+
+function TDerBitString.GetInt32Value: Int32;
+var
+  Value, &length, I, mask: Int32;
+begin
+  Value := 0;
+  length := Min(4, System.length(mData));
+  for I := 0 to System.Pred(length) do
+  begin
+    Value := Value or (Int32(mData[I]) shl (8 * I));
+  end;
+
+  if ((mPadBits > 0) and (length = System.length(mData))) then
+  begin
+    mask := (1 shl mPadBits) - 1;
+    Value := Value and (not(mask shl (8 * (length - 1))));
+  end;
+  result := Value;
+end;
+
+function TDerBitString.GetString: String;
+var
+  buffer: TStringList;
+  I: Int32;
+  Str: TCryptoLibByteArray;
+  ubyte: UInt32;
+begin
+  buffer := TStringList.Create();
+  buffer.LineBreak := '';
+  Str := GetDerEncoded();
+  buffer.Add('#');
+  I := 0;
+  try
+    while I <> System.length(Str) do
+    begin
+      ubyte := Str[I];
+      buffer.Add(FTable[(ubyte shr 4) and $F]);
+      buffer.Add(FTable[Str[I] and $F]);
+      System.Inc(I);
+    end;
+    result := buffer.Text;
+  finally
+    buffer.Free;
+  end;
+end;
+
+function TDerBitString.Asn1GetHashCode: Int32;
+begin
+  result := mPadBits xor TArrayUtils.GetArrayHashCode(mData);
+end;
+
+constructor TDerBitString.Create(const obj: IAsn1Encodable);
+begin
+  Create(obj.GetDerEncoded());
+end;
+
+{ TBerBitString }
+
+constructor TBerBitString.Create(const data: TCryptoLibByteArray);
+begin
+  Inherited Create(data);
+end;
+
+constructor TBerBitString.Create(const data: TCryptoLibByteArray;
+  padBits: Int32);
+begin
+  Inherited Create(data, padBits);
+end;
+
+constructor TBerBitString.Create(const obj: IAsn1Encodable);
+begin
+  Inherited Create(obj);
+end;
+
+constructor TBerBitString.Create(namedBits: Int32);
+begin
+  Inherited Create(namedBits);
+end;
+
+procedure TBerBitString.Encode(const derOut: TStream);
+begin
+  if ((derOut is TAsn1OutputStream) or (derOut is TBerOutputStream)) then
+  begin
+    (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.BitString,
+      Byte(mPadBits), mData);
+  end
+  else
+  begin
+    Inherited Encode(derOut);
+  end;
+end;
+
+{ TBerGenerator }
+
+constructor TBerGenerator.Create(outStream: TStream);
+begin
+  Inherited Create(outStream);
+end;
+
+procedure TBerGenerator.AddObject(const obj: IAsn1Encodable);
+var
+  temp: TBerOutputStream;
+begin
+  temp := TBerOutputStream.Create(&Out);
+  try
+    temp.WriteObject(obj);
+  finally
+    temp.Free;
+  end;
+end;
+
+procedure TBerGenerator.Close;
+begin
+  WriteBerEnd();
+end;
+
+constructor TBerGenerator.Create(outStream: TStream; tagNo: Int32;
+  isExplicit: Boolean);
+begin
+  Inherited Create(outStream);
+  F_tagged := True;
+  F_isExplicit := isExplicit;
+  F_tagNo := tagNo;
+end;
+
+function TBerGenerator.GetRawOutputStream: TStream;
+begin
+  result := &Out;
+end;
+
+procedure TBerGenerator.WriteBerBody(contentStream: TStream);
+begin
+  TStreamUtils.PipeAll(contentStream, &Out);
+end;
+
+procedure TBerGenerator.WriteBerEnd;
+begin
+  &Out.WriteByte($00);
+  &Out.WriteByte($00);
+
+  if (F_tagged and F_isExplicit) then // write extra end for tag header
+  begin
+    &Out.WriteByte($00);
+    &Out.WriteByte($00);
+  end;
+end;
+
+procedure TBerGenerator.WriteBerHeader(tag: Int32);
+var
+  tagNum: Int32;
+begin
+  if (F_tagged) then
+  begin
+    tagNum := F_tagNo or TAsn1Tags.Tagged;
+
+    if (F_isExplicit) then
+    begin
+      WriteHdr(tagNum or TAsn1Tags.Constructed);
+      WriteHdr(tag);
+    end
+    else
+    begin
+      if ((tag and TAsn1Tags.Constructed) <> 0) then
+      begin
+        WriteHdr(tagNum or TAsn1Tags.Constructed);
+      end
+      else
+      begin
+        WriteHdr(tagNum);
+      end;
+    end
+  end
+  else
+  begin
+    WriteHdr(tag);
+  end;
+end;
+
+procedure TBerGenerator.WriteHdr(tag: Int32);
+begin
+  &Out.WriteByte(Byte(tag));
+  &Out.WriteByte($80);
+end;
+
+{ TBerNull }
+
+constructor TBerNull.Create(dummy: Int32);
+begin
+  Inherited Create(dummy);
+end;
+
+procedure TBerNull.Encode(const derOut: TStream);
+begin
+
+  if ((derOut is TAsn1OutputStream) or (derOut is TBerOutputStream)) then
+  begin
+    (derOut as TDerOutputStream).WriteByte(TAsn1Tags.Null);
+  end
+  else
+  begin
+    Inherited Encode(derOut);
+  end;
+end;
+
+class function TBerNull.GetInstance: IBerNull;
+begin
+  result := TBerNull.Create(0);
+end;
+
+{ TBerSequenceGenerator }
+
+constructor TBerSequenceGenerator.Create(outStream: TStream);
+begin
+  Inherited Create(outStream);
+  WriteBerHeader(TAsn1Tags.Constructed or TAsn1Tags.Sequence);
+end;
+
+constructor TBerSequenceGenerator.Create(outStream: TStream; tagNo: Int32;
+  isExplicit: Boolean);
+begin
+  Inherited Create(outStream, tagNo, isExplicit);
+  WriteBerHeader(TAsn1Tags.Constructed or TAsn1Tags.Sequence);
+end;
+
+{ TBerSequenceParser }
+
+constructor TBerSequenceParser.Create(const parser: IAsn1StreamParser);
+begin
+  F_parser := parser;
+end;
+
+function TBerSequenceParser.ReadObject: IAsn1Convertible;
+begin
+  result := F_parser.ReadObject();
+end;
+
+function TBerSequenceParser.ToAsn1Object: IAsn1Object;
+begin
+  result := TBerSequence.Create(F_parser.ReadVector());
+end;
+
+{ TBerSet }
+
+class function TBerSet.GetEmpty: IBerSet;
+begin
+  result := TBerSet.Create();
+end;
+
+constructor TBerSet.Create;
+begin
+  Inherited Create();
+end;
+
+constructor TBerSet.Create(const v: IAsn1EncodableVector;
+  needsSorting: Boolean);
+begin
+  Inherited Create(v, needsSorting);
+end;
+
+destructor TBerSet.Destroy;
+begin
+  inherited Destroy;
+end;
+
+constructor TBerSet.Create(const obj: IAsn1Encodable);
+begin
+  Inherited Create(obj);
+end;
+
+constructor TBerSet.Create(const v: IAsn1EncodableVector);
+begin
+  Inherited Create(v, False);
+end;
+
+procedure TBerSet.Encode(const derOut: TStream);
+var
+  o: IAsn1Encodable;
+  LListAsn1Encodable: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  if ((derOut is TAsn1OutputStream) or (derOut is TBerOutputStream)) then
+  begin
+    (derOut as TDerOutputStream).WriteByte(TAsn1Tags.&Set or
+      TAsn1Tags.Constructed);
+
+    (derOut as TDerOutputStream).WriteByte($80);
+
+    LListAsn1Encodable := Self.GetEnumerable;
+    for o in LListAsn1Encodable do
+    begin
+      (derOut as TDerOutputStream).WriteObject(o);
+    end;
+
+    (derOut as TDerOutputStream).WriteByte($00);
+    (derOut as TDerOutputStream).WriteByte($00);
+  end
+  else
+  begin
+    (Inherited Encode(derOut));
+  end;
+end;
+
+class function TBerSet.FromVector(const v: IAsn1EncodableVector;
+  needsSorting: Boolean): IBerSet;
+begin
+  if v.count < 1 then
+  begin
+    result := Empty;
+  end
+  else
+  begin
+    result := TBerSet.Create(v, needsSorting);
+  end;
+end;
+
+class function TBerSet.FromVector(const v: IAsn1EncodableVector): IBerSet;
+begin
+  if v.count < 1 then
+  begin
+    result := Empty;
+  end
+  else
+  begin
+    result := TBerSet.Create(v);
+  end;
+end;
+
+{ TBerSetParser }
+
+constructor TBerSetParser.Create(const parser: IAsn1StreamParser);
+begin
+  F_parser := parser;
+end;
+
+function TBerSetParser.ReadObject: IAsn1Convertible;
+begin
+  result := F_parser.ReadObject();
+end;
+
+function TBerSetParser.ToAsn1Object: IAsn1Object;
+begin
+  result := TBerSet.Create(F_parser.ReadVector(), False);
+end;
+
+{ TDerTaggedObject }
+
+constructor TDerTaggedObject.Create(tagNo: Int32; const obj: IAsn1Encodable);
+begin
+  Inherited Create(tagNo, obj);
+end;
+
+constructor TDerTaggedObject.Create(explicitly: Boolean; tagNo: Int32;
+  const obj: IAsn1Encodable);
+begin
+  Inherited Create(explicitly, tagNo, obj)
+end;
+
+constructor TDerTaggedObject.Create(tagNo: Int32);
+begin
+  Inherited Create(False, tagNo, TDerSequence.Empty)
+end;
+
+procedure TDerTaggedObject.Encode(const derOut: TStream);
+var
+  bytes: TCryptoLibByteArray;
+  flags: Int32;
+begin
+  if (not IsEmpty()) then
+  begin
+    bytes := obj.GetDerEncoded();
+
+    if (explicitly) then
+    begin
+      (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.Constructed or
+        TAsn1Tags.Tagged, tagNo, bytes);
+    end
+    else
+    begin
+      //
+      // need to mark constructed types... (preserve Constructed tag)
+      //
+      flags := (bytes[0] and TAsn1Tags.Constructed) or TAsn1Tags.Tagged;
+      (derOut as TDerOutputStream).WriteTag(flags, tagNo);
+      derOut.Write(bytes[1], System.length(bytes) - 1);
+    end
+  end
+  else
+  begin
+    (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.Constructed or
+      TAsn1Tags.Tagged, tagNo, Nil);
+  end;
+end;
+
+{ TBerTaggedObject }
+
+constructor TBerTaggedObject.Create(tagNo: Int32; const obj: IAsn1Encodable);
+begin
+  Inherited Create(tagNo, obj);
+end;
+
+constructor TBerTaggedObject.Create(explicitly: Boolean; tagNo: Int32;
+  const obj: IAsn1Encodable);
+begin
+  Inherited Create(explicitly, tagNo, obj)
+end;
+
+constructor TBerTaggedObject.Create(tagNo: Int32);
+begin
+  Inherited Create(False, tagNo, TBerSequence.Empty)
+end;
+
+procedure TBerTaggedObject.Encode(const derOut: TStream);
+var
+  eObj: TList<IAsn1Encodable>;
+  LListIDerOctetString: TCryptoLibGenericArray<IDerOctetString>;
+  LListIAsn1Encodable: TCryptoLibGenericArray<IAsn1Encodable>;
+  asn1OctetString: IAsn1OctetString;
+  berOctetString: IBerOctetString;
+  derOctetString: IDerOctetString;
+  asn1Sequence: IAsn1Sequence;
+  asn1Set: IAsn1Set;
+  o: IAsn1Encodable;
+begin
+  eObj := TList<IAsn1Encodable>.Create();
+  try
+    if ((derOut is TAsn1OutputStream) or (derOut is TBerOutputStream)) then
+    begin
+      (derOut as TDerOutputStream)
+        .WriteTag(Byte(TAsn1Tags.Constructed or TAsn1Tags.Tagged), tagNo);
+      (derOut as TDerOutputStream).WriteByte($80);
+
+      if (not IsEmpty()) then
+      begin
+        if (not explicitly) then
+        begin
+          if (Supports(obj, IAsn1OctetString, asn1OctetString)) then
+          begin
+            if (Supports(asn1OctetString, IBerOctetString, berOctetString)) then
+            begin
+              LListIDerOctetString := berOctetString.GetEnumerable;
+              for derOctetString in LListIDerOctetString do
+              begin
+                eObj.Add(derOctetString as IAsn1Encodable);
+              end;
+            end
+            else
+            begin
+              berOctetString := TBerOctetString.Create
+                (asn1OctetString.GetOctets());
+              LListIDerOctetString := berOctetString.GetEnumerable;
+              for derOctetString in LListIDerOctetString do
+              begin
+                eObj.Add(derOctetString as IAsn1Encodable);
+              end;
+            end
+          end
+          else if Supports(obj, IAsn1Sequence, asn1Sequence) then
+          begin
+            LListIAsn1Encodable := asn1Sequence.GetEnumerable;
+            for o in LListIAsn1Encodable do
+            begin
+              eObj.Add(o);
+            end;
+          end
+          else if Supports(obj, IAsn1Set, asn1Set) then
+          begin
+            LListIAsn1Encodable := asn1Set.GetEnumerable;
+            for o in LListIAsn1Encodable do
+            begin
+              eObj.Add(o);
+            end;
+          end
+          else
+          begin
+            raise ENotImplementedCryptoLibException.CreateResFmt
+              (@SNotImplemented, [(obj as TAsn1Encodable).ClassName]);
+          end;
+
+          for o in eObj do
+          begin
+            (derOut as TDerOutputStream).WriteObject(o);
+          end;
+        end
+        else
+        begin
+          (derOut as TDerOutputStream).WriteObject(obj);
+        end;
+      end;
+
+      (derOut as TDerOutputStream).WriteByte($00);
+      (derOut as TDerOutputStream).WriteByte($00);
+    end
+    else
+    begin
+      (Inherited Encode(derOut));
+    end
+  finally
+    eObj.Free;
+  end;
+
+end;
+
+{ TBerTaggedObjectParser }
+
+constructor TBerTaggedObjectParser.Create(Constructed: Boolean;
+  tagNumber: Int32; const parser: IAsn1StreamParser);
+begin
+  F_constructed := Constructed;
+  F_tagNumber := tagNumber;
+  F_parser := parser;
+end;
+
+destructor TBerTaggedObjectParser.Destroy;
+begin
+  F_parser := Nil;
+  inherited Destroy;
+end;
+
+function TBerTaggedObjectParser.GetIsConstructed: Boolean;
+begin
+  result := F_constructed;
+end;
+
+function TBerTaggedObjectParser.GetObjectParser(tag: Int32; isExplicit: Boolean)
+  : IAsn1Convertible;
+begin
+  if (isExplicit) then
+  begin
+    if (not F_constructed) then
+    begin
+      raise EIOCryptoLibException.CreateRes(@SUnConstructedTag);
+    end;
+
+    result := F_parser.ReadObject();
+    Exit;
+  end;
+
+  result := F_parser.ReadImplicit(F_constructed, tag);
+end;
+
+function TBerTaggedObjectParser.GetTagNo: Int32;
+begin
+  result := F_tagNumber;
+end;
+
+function TBerTaggedObjectParser.ToAsn1Object: IAsn1Object;
+begin
+  try
+    result := F_parser.ReadTaggedObject(F_constructed, F_tagNumber);
+  except
+    on e: EIOCryptoLibException do
+    begin
+      raise EAsn1ParsingCryptoLibException.CreateResFmt(@SParsingError,
+        [e.Message]);
+    end;
+
+  end;
+end;
+
+{ TDerBmpString }
+
+function TDerBmpString.GetStr: String;
+begin
+  result := FStr;
+end;
+
+function TDerBmpString.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerBmpString;
+begin
+
+  if (not Supports(asn1Object, IDerBmpString, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := Str = other.Str;
+end;
+
+constructor TDerBmpString.Create(const astr: TCryptoLibByteArray);
+var
+  cs: TCryptoLibCharArray;
+  I: Int32;
+begin
+  Inherited Create();
+  if (astr = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SEmptyInput);
+  end;
+
+  System.SetLength(cs, System.length(astr) shr 1);
+
+  I := 0;
+
+  while I <> System.length(cs) do
+  begin
+    cs[I] := Char((astr[2 * I] shl 8) or (astr[2 * I + 1] and $FF));
+    System.Inc(I);
+  end;
+
+  System.SetString(FStr, PChar(@cs[0]), System.length(cs));
+end;
+
+constructor TDerBmpString.Create(const astr: String);
+begin
+  Inherited Create();
+  if (astr = '') then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SEmptyInput);
+  end;
+
+  FStr := astr;
+end;
+
+procedure TDerBmpString.Encode(const derOut: TStream);
+var
+  c: TCryptoLibCharArray;
+  b: TCryptoLibByteArray;
+  I, LowPoint, HighPoint: Int32;
+begin
+  System.SetLength(c, System.length(Str));
+
+  // had to use this loop because somehow, StrPLCopy causes memory leak in FPC v3.0.5
+{$IFDEF DELPHIXE3_UP}
+  LowPoint := System.Low(Str);
+  HighPoint := System.High(Str);
+{$ELSE}
+  LowPoint := 1;
+  HighPoint := System.length(Str);
+{$ENDIF DELPHIXE3_UP}
+  for I := LowPoint to HighPoint do
+  begin
+    c[I - 1] := Str[I];
+  end;
+  System.SetLength(b, System.length(c) * 2);
+
+  I := 0;
+
+  while I <> System.length(c) do
+  begin
+    b[2 * I] := Byte(Ord(c[I]) shr 8);
+    b[2 * I + 1] := Byte(c[I]);
+    System.Inc(I);
+  end;
+
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.BmpString, b);
+
+end;
+
+class function TDerBmpString.GetInstance(const obj: TObject): IDerBmpString;
+begin
+  if ((obj = Nil) or (obj is TDerBmpString)) then
+  begin
+    result := obj as TDerBmpString;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerBmpString.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerBmpString;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerBmpString))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := TDerBmpString.Create(TAsn1OctetString.GetInstance(o as TAsn1Object)
+    .GetOctets());
+end;
+
+function TDerBmpString.GetString: String;
+begin
+  result := FStr;
+end;
+
+{ TDerBoolean }
+
+function TDerBoolean.GetIsTrue: Boolean;
+begin
+  result := Fvalue <> 0;
+end;
+
+function TDerBoolean.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerBoolean;
+begin
+
+  if (not Supports(asn1Object, IDerBoolean, other)) then
+  begin
+    result := System.False;
+    Exit;
+  end;
+
+  result := IsTrue = other.IsTrue;
+end;
+
+function TDerBoolean.Asn1GetHashCode: Int32;
+begin
+  result := Ord(IsTrue);
+end;
+
+constructor TDerBoolean.Create(const val: TCryptoLibByteArray);
+begin
+  Inherited Create();
+  if (System.length(val) <> 1) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidValue);
+  end;
+
+  // TODO Are there any constraints on the possible byte values?
+  Fvalue := val[0];
+end;
+
+constructor TDerBoolean.Create(Value: Boolean);
+begin
+  Inherited Create();
+  if Value then
+  begin
+    Fvalue := Byte($FF)
+  end
+  else
+  begin
+    Fvalue := Byte(0)
+  end;
+end;
+
+procedure TDerBoolean.Encode(const derOut: TStream);
+begin
+  // TODO Should we make sure the byte value is one of '0' or '0xff' here?
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.Boolean,
+    TCryptoLibByteArray.Create(Fvalue));
+end;
+
+class function TDerBoolean.FromOctetString(const Value: TCryptoLibByteArray)
+  : IDerBoolean;
+var
+  b: Byte;
+begin
+  if (System.length(Value) <> 1) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidBooleanValue);
+  end;
+
+  b := Value[0];
+
+  case b of
+    0:
+      result := TDerBoolean.False;
+    $FF:
+      result := TDerBoolean.True
+  else
+    begin
+      result := TDerBoolean.Create(Value);
+    end;
+  end;
+
+end;
+
+class function TDerBoolean.GetInstance(Value: Boolean): IDerBoolean;
+begin
+  if Value then
+  begin
+    result := TDerBoolean.True;
+  end
+  else
+  begin
+    result := TDerBoolean.False;
+  end;
+end;
+
+class function TDerBoolean.GetInstance(const obj: TObject): IDerBoolean;
+begin
+  if ((obj = Nil) or (obj is TDerBoolean)) then
+  begin
+    Supports(obj, IDerBoolean, result);
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerBoolean.GetFalse: IDerBoolean;
+begin
+  result := TDerBoolean.Create(System.False);
+end;
+
+class function TDerBoolean.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerBoolean;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerBoolean))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := FromOctetString((o as IAsn1OctetString).GetOctets());
+end;
+
+class function TDerBoolean.GetTrue: IDerBoolean;
+begin
+  result := TDerBoolean.Create(System.True);
+end;
+
+function TDerBoolean.ToString: String;
+begin
+  result := BoolToStr(IsTrue, System.True);
+end;
+
+{ TDerEnumerated }
+
+function TDerEnumerated.GetBytes: TCryptoLibByteArray;
+begin
+  result := Fbytes;
+end;
+
+function TDerEnumerated.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerEnumerated;
+begin
+
+  if (not Supports(asn1Object, IDerEnumerated, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := TArrayUtils.AreEqual(bytes, other.bytes);
+end;
+
+function TDerEnumerated.Asn1GetHashCode: Int32;
+begin
+  result := TArrayUtils.GetArrayHashCode(bytes);
+end;
+
+constructor TDerEnumerated.Create(val: Int32);
+begin
+  Inherited Create();
+  Fbytes := TBigInteger.ValueOf(val).ToByteArray();
+end;
+
+constructor TDerEnumerated.Create(const val: TBigInteger);
+begin
+  Inherited Create();
+  Fbytes := val.ToByteArray();
+end;
+
+constructor TDerEnumerated.Create(const bytes: TCryptoLibByteArray);
+begin
+  Inherited Create();
+  if (System.length(bytes) > 1) then
+  begin
+    if ((bytes[0] = 0) and ((bytes[1] and $80) = 0)) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SMalformedEnumerated);
+    end;
+    if ((bytes[0] = Byte($FF)) and ((bytes[1] and $80) <> 0)) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SMalformedEnumerated);
+    end;
+  end;
+  Fbytes := System.Copy(bytes);
+end;
+
+procedure TDerEnumerated.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.Enumerated, Fbytes);
+end;
+
+class function TDerEnumerated.FromOctetString(const enc: TCryptoLibByteArray)
+  : IDerEnumerated;
+var
+  LValue: Int32;
+  cached: IDerEnumerated;
+begin
+  if (System.length(enc) = 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SZeroLength);
+  end;
+
+  if (System.length(enc) = 1) then
+  begin
+    LValue := enc[0];
+    if (LValue < System.length(Fcache)) then
+    begin
+      cached := Fcache[LValue];
+      if (cached <> Nil) then
+      begin
+        result := cached;
+        Exit;
+      end;
+      Fcache[LValue] := TDerEnumerated.Create(System.Copy(enc));
+      result := Fcache[LValue];
+      Exit;
+    end;
+  end;
+
+  result := TDerEnumerated.Create(System.Copy(enc));
+end;
+
+class function TDerEnumerated.GetInstance(const obj: TObject): IDerEnumerated;
+begin
+  if ((obj = Nil) or (obj is TDerEnumerated)) then
+  begin
+    result := obj as TDerEnumerated;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerEnumerated.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerEnumerated;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if (isExplicit or (Supports(o, IDerEnumerated))) then
+  begin
+    result := GetInstance(o as TObject);
+    Exit;
+  end;
+
+  result := FromOctetString((o as IAsn1OctetString).GetOctets());
+end;
+
+function TDerEnumerated.GetValue: TBigInteger;
+begin
+  result := TBigInteger.Create(Fbytes);
+end;
+
+{ TDerGraphicString }
+
+function TDerGraphicString.GetmString: TCryptoLibByteArray;
+begin
+  result := FmString;
+end;
+
+function TDerGraphicString.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerGraphicString;
+begin
+
+  if (not Supports(asn1Object, IDerGraphicString, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := TArrayUtils.AreEqual(mString, other.mString);
+end;
+
+function TDerGraphicString.Asn1GetHashCode: Int32;
+begin
+  result := TArrayUtils.GetArrayHashCode(mString);
+end;
+
+constructor TDerGraphicString.Create(const encoding: TCryptoLibByteArray);
+begin
+  Inherited Create();
+  FmString := System.Copy(encoding);
+end;
+
+procedure TDerGraphicString.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.GraphicString, mString);
+end;
+
+class function TDerGraphicString.GetInstance(const obj: TObject)
+  : IDerGraphicString;
+begin
+  if ((obj = Nil) or (obj is TDerGraphicString)) then
+  begin
+    result := obj as TDerGraphicString;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerGraphicString.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerGraphicString;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerGraphicString))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := TDerGraphicString.Create
+    (TAsn1OctetString.GetInstance(o as TAsn1Object).GetOctets());
+end;
+
+class function TDerGraphicString.GetInstance(const obj: TCryptoLibByteArray)
+  : IDerGraphicString;
+begin
+  try
+    result := FromByteArray(obj) as IDerGraphicString;
+  except
+    on e: Exception do
+    begin
+      raise EArgumentCryptoLibException.CreateResFmt(@SEncodingError,
+        [e.Message]);
+    end;
+
+  end;
+end;
+
+function TDerGraphicString.GetOctets: TCryptoLibByteArray;
+begin
+  result := System.Copy(mString);
+end;
+
+function TDerGraphicString.GetString: String;
+begin
+  result := TConverters.ConvertBytesToString(mString, TEncoding.ANSI);
+end;
+
+{ TDerExternal }
+
+class function TDerExternal.GetObjFromVector(const v: IAsn1EncodableVector;
+  Index: Int32): IAsn1Object;
+var
+  val: IAsn1Encodable;
+begin
+  if (v.count <= index) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SFewObject);
+  end;
+
+  val := v[index];
+  result := val.ToAsn1Object();
+end;
+
+class procedure TDerExternal.WriteEncodable(ms: TMemoryStream;
+  const e: IAsn1Encodable);
+var
+  bs: TCryptoLibByteArray;
+begin
+  if (e <> Nil) then
+  begin
+    bs := e.GetDerEncoded();
+    ms.Write(bs[0], System.length(bs));
+  end;
+end;
+
+function TDerExternal.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerExternal;
+begin
+  if (Self.Equals(asn1Object)) then
+  begin
+    result := True;
+    Exit;
+  end;
+
+  if (not Supports(asn1Object, IDerExternal, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := directReference.Equals(other.directReference) and
+    indirectReference.Equals(other.indirectReference) and
+    dataValueDescriptor.Equals(other.dataValueDescriptor) and
+    ExternalContent.Equals(other.ExternalContent);
+end;
+
+function TDerExternal.Asn1GetHashCode: Int32;
+var
+  ret: Int32;
+begin
+  ret := ExternalContent.GetHashCode();
+  if (directReference <> Nil) then
+  begin
+    ret := ret xor directReference.GetHashCode();
+  end;
+  if (indirectReference <> Nil) then
+  begin
+    ret := ret xor indirectReference.GetHashCode();
+  end;
+  if (dataValueDescriptor <> Nil) then
+  begin
+    ret := ret xor dataValueDescriptor.GetHashCode();
+  end;
+  result := ret;
+end;
+
+constructor TDerExternal.Create(const directReference: IDerObjectIdentifier;
+  const indirectReference: IDerInteger; const dataValueDescriptor: IAsn1Object;
+  encoding: Int32; const externalData: IAsn1Object);
+begin
+  Inherited Create();
+  FdirectReference := directReference;
+  FindirectReference := indirectReference;
+  FdataValueDescriptor := dataValueDescriptor;
+  Fencoding := encoding;
+  FexternalContent := externalData.ToAsn1Object();
+end;
+
+constructor TDerExternal.Create(const vector: IAsn1EncodableVector);
+var
+  offset: Int32;
+  enc: IAsn1Object;
+  derObjectIdentifier: IDerObjectIdentifier;
+  derInteger: IDerInteger;
+  obj: IAsn1TaggedObject;
+begin
+  Inherited Create();
+  offset := 0;
+  enc := GetObjFromVector(vector, offset);
+
+  if (Supports(enc, IDerObjectIdentifier, derObjectIdentifier)) then
+  begin
+    directReference := derObjectIdentifier;
+    System.Inc(offset);
+    enc := GetObjFromVector(vector, offset);
+  end;
+
+  if (Supports(enc, IDerInteger, derInteger)) then
+  begin
+    indirectReference := derInteger;
+    System.Inc(offset);
+    enc := GetObjFromVector(vector, offset);
+  end;
+  if (not(Supports(enc, IAsn1TaggedObject))) then
+  begin
+    dataValueDescriptor := enc;
+    System.Inc(offset);
+    enc := GetObjFromVector(vector, offset);
+  end;
+
+  if (vector.count <> (offset + 1)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SVectorTooLarge);
+  end;
+
+  if (not(Supports(enc, IAsn1TaggedObject, obj))) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SNoTaggedObjectFound);
+  end;
+
+  // Use property accessor to include check on value
+  encoding := obj.tagNo;
+
+  if ((Fencoding < 0) or (Fencoding > 2)) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes(@SInvalidEncodingValue);
+  end;
+
+  FexternalContent := obj.GetObject();
+end;
+
+constructor TDerExternal.Create(const directReference: IDerObjectIdentifier;
+  const indirectReference: IDerInteger; const dataValueDescriptor: IAsn1Object;
+  const externalData: IDerTaggedObject);
+begin
+  Create(directReference, indirectReference, dataValueDescriptor,
+    externalData.tagNo, externalData.ToAsn1Object());
+end;
+
+procedure TDerExternal.Encode(const derOut: TStream);
+var
+  ms: TMemoryStream;
+  buffer: TCryptoLibByteArray;
+begin
+  ms := TMemoryStream.Create();
+  try
+    WriteEncodable(ms, directReference);
+    WriteEncodable(ms, indirectReference);
+    WriteEncodable(ms, dataValueDescriptor);
+    WriteEncodable(ms, TDerTaggedObject.Create(TAsn1Tags.External,
+      ExternalContent));
+
+    System.SetLength(buffer, ms.Size);
+    ms.Position := 0;
+    ms.Read(buffer[0], ms.Size);
+    (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.Constructed,
+      TAsn1Tags.External, buffer);
+  finally
+    ms.Free;
+  end;
+end;
+
+function TDerExternal.GetDataValueDescriptor: IAsn1Object;
+begin
+  result := FdataValueDescriptor;
+end;
+
+function TDerExternal.GetDirectReference: IDerObjectIdentifier;
+begin
+  result := FdirectReference;
+end;
+
+function TDerExternal.GetEncoding: Int32;
+begin
+  result := Fencoding;
+end;
+
+function TDerExternal.GetExternalContent: IAsn1Object;
+begin
+  result := FexternalContent;
+end;
+
+function TDerExternal.GetIndirectReference: IDerInteger;
+begin
+  result := FindirectReference;
+end;
+
+procedure TDerExternal.SetDataValueDescriptor(const Value: IAsn1Object);
+begin
+  FdataValueDescriptor := Value;
+end;
+
+procedure TDerExternal.SetDirectReference(const Value: IDerObjectIdentifier);
+begin
+  FdirectReference := Value;
+end;
+
+procedure TDerExternal.SetEncoding(const Value: Int32);
+begin
+  if ((Fencoding < 0) or (Fencoding > 2)) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateResFmt
+      (@SInvalidEncoding, [Value]);
+  end;
+
+  Fencoding := Value;
+end;
+
+procedure TDerExternal.SetExternalContent(const Value: IAsn1Object);
+begin
+  FexternalContent := Value;
+end;
+
+procedure TDerExternal.SetIndirectReference(const Value: IDerInteger);
+begin
+  FindirectReference := Value;
+end;
+
+{ TDerInteger }
+
+function TDerInteger.GetBytes: TCryptoLibByteArray;
+begin
+  result := Fbytes;
+end;
+
+class function TDerInteger.GetInstance(const obj: TObject): IDerInteger;
+begin
+  if ((obj = Nil) or (obj is TDerInteger)) then
+  begin
+    result := obj as TDerInteger;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+
+end;
+
+function TDerInteger.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerInteger;
+begin
+
+  if (not Supports(asn1Object, IDerInteger, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := TArrayUtils.AreEqual(bytes, other.bytes);
+end;
+
+function TDerInteger.Asn1GetHashCode: Int32;
+begin
+  result := TArrayUtils.GetArrayHashCode(Fbytes);
+end;
+
+constructor TDerInteger.Create(const Value: TBigInteger);
+begin
+  inherited Create();
+  if (not Value.IsInitialized) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SValueNil);
+  end;
+
+  Fbytes := Value.ToByteArray();
+end;
+
+constructor TDerInteger.Create(Value: Int32);
+begin
+  inherited Create();
+  Fbytes := TBigInteger.ValueOf(Value).ToByteArray();
+end;
+
+constructor TDerInteger.Create(const bytes: TCryptoLibByteArray);
+begin
+  inherited Create();
+  if (System.length(bytes) > 1) then
+  begin
+    if ((bytes[0] = 0) and ((bytes[1] and $80) = 0)) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SMalformedInteger);
+    end;
+    if ((bytes[0] = Byte($FF)) and ((bytes[1] and $80) <> 0)) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SMalformedInteger);
+    end;
+  end;
+  Fbytes := System.Copy(bytes);
+end;
+
+procedure TDerInteger.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.Integer, Fbytes);
+end;
+
+class function TDerInteger.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerInteger;
+var
+  o: IAsn1Object;
+begin
+  if (obj = Nil) then
+    raise EArgumentNilCryptoLibException.CreateRes(@SObjectNil);
+
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerInteger))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := TDerInteger.Create(TAsn1OctetString.GetInstance(o as TAsn1Object)
+    .GetOctets());
+
+end;
+
+function TDerInteger.GetPositiveValue: TBigInteger;
+begin
+  result := TBigInteger.Create(1, Fbytes);
+end;
+
+function TDerInteger.GetValue: TBigInteger;
+begin
+  result := TBigInteger.Create(Fbytes);
+end;
+
+function TDerInteger.ToString: String;
+begin
+  result := Value.ToString();
+end;
+
+{ TDerExternalParser }
+
+constructor TDerExternalParser.Create(const parser: IAsn1StreamParser);
+begin
+  Inherited Create();
+  F_parser := parser;
+end;
+
+function TDerExternalParser.ReadObject: IAsn1Convertible;
+begin
+  result := F_parser.ReadObject();
+end;
+
+function TDerExternalParser.ToAsn1Object: IAsn1Object;
+begin
+  result := TDerExternal.Create(F_parser.ReadVector());
+end;
+
+{ TDerOctetStringParser }
+
+constructor TDerOctetStringParser.Create(stream: TStream);
+begin
+  FStream := stream;
+end;
+
+destructor TDerOctetStringParser.Destroy;
+begin
+  FStream.Free;
+  inherited Destroy;
+end;
+
+function TDerOctetStringParser.GetOctetStream: TStream;
+begin
+  result := FStream;
+end;
+
+function TDerOctetStringParser.ToAsn1Object: IAsn1Object;
+begin
+  try
+    result := TDerOctetString.Create((FStream as TDefiniteLengthInputStream)
+      .ToArray());
+  except
+    on e: EIOCryptoLibException do
+    begin
+      raise EInvalidOperationCryptoLibException.CreateResFmt(@SConvertError,
+        [e.Message]);
+    end;
+  end;
+end;
+
+{ TDerGeneralString }
+
+function TDerGeneralString.GetStr: String;
+begin
+  result := FStr;
+end;
+
+function TDerGeneralString.GetOctets: TCryptoLibByteArray;
+begin
+  result := TConverters.ConvertStringToBytes(Str, TEncoding.ASCII);
+end;
+
+function TDerGeneralString.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerGeneralString;
+begin
+
+  if (not Supports(asn1Object, IDerGeneralString, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := Str = other.Str;
+end;
+
+constructor TDerGeneralString.Create(const Str: TCryptoLibByteArray);
+begin
+  Create(TConverters.ConvertBytesToString(Str, TEncoding.ASCII));
+end;
+
+constructor TDerGeneralString.Create(const Str: String);
+begin
+  Inherited Create();
+  if (Str = '') then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SStrNil);
+  end;
+
+  FStr := Str;
+end;
+
+procedure TDerGeneralString.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.GeneralString,
+    GetOctets());
+end;
+
+class function TDerGeneralString.GetInstance(const obj: TObject)
+  : IDerGeneralString;
+begin
+  if ((obj = Nil) or (obj is TDerGeneralString)) then
+  begin
+    result := obj as TDerGeneralString;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerGeneralString.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerGeneralString;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerGeneralString))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := TDerGeneralString.Create
+    (TAsn1OctetString.GetInstance(o as TAsn1Object).GetOctets());
+end;
+
+function TDerGeneralString.GetString: String;
+begin
+  result := Str;
+end;
+
+{ TDerGenerator }
+
+constructor TDerGenerator.Create(const outStream: TStream);
+begin
+  Inherited Create(outStream);
+end;
+
+constructor TDerGenerator.Create(const outStream: TStream; tagNo: Int32;
+  isExplicit: Boolean);
+begin
+  Inherited Create(outStream);
+  F_tagged := True;
+  F_isExplicit := isExplicit;
+  F_tagNo := tagNo;
+end;
+
+class procedure TDerGenerator.WriteDerEncoded(const outStream: TStream;
+  tag: Int32; const bytes: TCryptoLibByteArray);
+begin
+  outStream.WriteByte(Byte(tag));
+  WriteLength(outStream, System.length(bytes));
+  outStream.Write(bytes[0], System.length(bytes));
+end;
+
+procedure TDerGenerator.WriteDerEncoded(tag: Int32;
+  const bytes: TCryptoLibByteArray);
+var
+  tagNum, newTag: Int32;
+  bOut: TMemoryStream;
+  temp: TCryptoLibByteArray;
+begin
+  if (F_tagged) then
+  begin
+    tagNum := F_tagNo or TAsn1Tags.Tagged;
+
+    if (F_isExplicit) then
+    begin
+      newTag := F_tagNo or TAsn1Tags.Constructed or TAsn1Tags.Tagged;
+      bOut := TMemoryStream.Create();
+      try
+        WriteDerEncoded(bOut, tag, bytes);
+        bOut.Position := 0;
+        System.SetLength(temp, bOut.Size);
+        bOut.Read(temp[0], bOut.Size);
+        WriteDerEncoded(&Out, newTag, temp);
+      finally
+        bOut.Free;
+      end;
+    end
+    else
+    begin
+      if ((tag and TAsn1Tags.Constructed) <> 0) then
+      begin
+        tagNum := tagNum or TAsn1Tags.Constructed;
+      end;
+
+      WriteDerEncoded(&Out, tagNum, bytes);
+    end;
+  end
+  else
+  begin
+    WriteDerEncoded(&Out, tag, bytes);
+  end;
+end;
+
+class procedure TDerGenerator.WriteDerEncoded(const outStr: TStream; tag: Int32;
+  const inStr: TStream);
+begin
+  WriteDerEncoded(outStr, tag, TStreamUtils.ReadAll(inStr));
+end;
+
+class procedure TDerGenerator.WriteLength(const outStr: TStream; length: Int32);
+var
+  Size, val, I: Int32;
+begin
+  if (length > 127) then
+  begin
+    Size := 1;
+    val := length;
+
+    val := TBits.Asr32(val, 8);
+    while (val <> 0) do
+    begin
+      System.Inc(Size);
+      val := TBits.Asr32(val, 8);
+    end;
+
+    outStr.WriteByte(Byte(Size or $80));
+
+    I := (Size - 1) * 8;
+
+    while I >= 0 do
+    begin
+      outStr.WriteByte(Byte(TBits.Asr32(length, I)));
+      System.Dec(I, 8);
+    end;
+  end
+  else
+  begin
+    outStr.WriteByte(Byte(length));
+  end;
+end;
+
+{ TDerIA5String }
+
+function TDerIA5String.GetStr: String;
+begin
+  result := FStr;
+end;
+
+function TDerIA5String.GetOctets: TCryptoLibByteArray;
+begin
+  result := TConverters.ConvertStringToBytes(Str, TEncoding.ASCII);
+end;
+
+class function TDerIA5String.IsIA5String(const Str: String): Boolean;
+var
+  ch: Char;
+begin
+  for ch in Str do
+  begin
+    if (Ord(ch) > $007F) then
+    begin
+      result := False;
+      Exit;
+    end;
+  end;
+
+  result := True;
+end;
+
+function TDerIA5String.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerIA5String;
+begin
+
+  if (not Supports(asn1Object, IDerIA5String, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := Str = other.Str;
+end;
+
+function TDerIA5String.Asn1GetHashCode: Int32;
+begin
+  result := TStringUtils.GetStringHashCode(FStr);
+end;
+
+constructor TDerIA5String.Create(const Str: TCryptoLibByteArray);
+begin
+  Create(TConverters.ConvertBytesToString(Str, TEncoding.ASCII), False);
+end;
+
+constructor TDerIA5String.Create(const Str: String);
+begin
+  Create(Str, False);
+end;
+
+constructor TDerIA5String.Create(const Str: String; validate: Boolean);
+begin
+  Inherited Create();
+  if (Str = '') then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SStrNil);
+  end;
+  if (validate and (not IsIA5String(Str))) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SIllegalCharacters);
+  end;
+
+  FStr := Str;
+end;
+
+procedure TDerIA5String.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.IA5String, GetOctets());
+end;
+
+class function TDerIA5String.GetInstance(const obj: TObject): IDerIA5String;
+begin
+  if ((obj = Nil) or (obj is TDerIA5String)) then
+  begin
+    result := obj as TDerIA5String;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerIA5String.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerIA5String;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerIA5String))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := TDerIA5String.Create(TAsn1OctetString.GetInstance(o as TAsn1Object)
+    .GetOctets());
+end;
+
+function TDerIA5String.GetString: String;
+begin
+  result := Str;
+end;
+
+{ TDerNumericString }
+
+function TDerNumericString.GetStr: String;
+begin
+  result := FStr;
+end;
+
+function TDerNumericString.GetOctets: TCryptoLibByteArray;
+begin
+  result := TConverters.ConvertStringToBytes(Str, TEncoding.ASCII);
+end;
+
+class function TDerNumericString.IsNumericString(const Str: String): Boolean;
+var
+  ch: Char;
+begin
+  for ch in Str do
+  begin
+    // char.IsDigit(ch)
+    if ((Ord(ch) > $007F) or ((ch <> ' ') and (not CharInSet(ch, ['0' .. '9']))))
+    then
+    begin
+      result := False;
+      Exit;
+    end;
+  end;
+
+  result := True;
+end;
+
+function TDerNumericString.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerNumericString;
+begin
+
+  if (not Supports(asn1Object, IDerNumericString, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := Str = other.Str;
+end;
+
+constructor TDerNumericString.Create(const Str: TCryptoLibByteArray);
+begin
+  Create(TConverters.ConvertBytesToString(Str, TEncoding.ASCII), False);
+end;
+
+constructor TDerNumericString.Create(const Str: String);
+begin
+  Create(Str, False);
+end;
+
+constructor TDerNumericString.Create(const Str: String; validate: Boolean);
+begin
+  Inherited Create();
+  if (Str = '') then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SStrNil);
+  end;
+  if (validate and (not IsNumericString(Str))) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SIllegalCharacters);
+  end;
+
+  FStr := Str;
+end;
+
+procedure TDerNumericString.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.NumericString,
+    GetOctets());
+end;
+
+class function TDerNumericString.GetInstance(const obj: TObject)
+  : IDerNumericString;
+begin
+  if ((obj = Nil) or (obj is TDerNumericString)) then
+  begin
+    result := obj as TDerNumericString;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerNumericString.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerNumericString;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerNumericString))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := TDerNumericString.Create
+    (TAsn1OctetString.GetInstance(o as TAsn1Object).GetOctets());
+end;
+
+function TDerNumericString.GetString: String;
+begin
+  result := Str;
+end;
+
+{ TDerPrintableString }
+
+function TDerPrintableString.GetStr: String;
+begin
+  result := FStr;
+end;
+
+function TDerPrintableString.GetString: String;
+begin
+  result := Str;
+end;
+
+function TDerPrintableString.GetOctets: TCryptoLibByteArray;
+begin
+  result := TConverters.ConvertStringToBytes(Str, TEncoding.ASCII);
+end;
+
+class function TDerPrintableString.IsPrintableString(const Str: String)
+  : Boolean;
+var
+  ch: Char;
+begin
+  for ch in Str do
+  begin
+
+    if ((Ord(ch) > $007F)) then
+    begin
+      result := False;
+      Exit;
+    end;
+
+    // if (char.IsLetterOrDigit(ch))
+    if CharInSet(ch, ['a' .. 'z', 'A' .. 'Z', '0' .. '9']) then
+    begin
+      continue;
+    end;
+
+    case IndexStr(UnicodeString(ch), [''' ''', '\', '(', ')', '+', '-', '.',
+      ':', '=', '?', '/', ',']) of
+      0 .. 11:
+        begin
+          continue;
+        end;
+    end;
+
+    result := False;
+    Exit;
+  end;
+
+  result := True;
+end;
+
+function TDerPrintableString.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerPrintableString;
+begin
+
+  if (not Supports(asn1Object, IDerPrintableString, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := Str = other.Str;
+end;
+
+constructor TDerPrintableString.Create(const Str: TCryptoLibByteArray);
+begin
+  Create(TConverters.ConvertBytesToString(Str, TEncoding.ASCII), False);
+end;
+
+constructor TDerPrintableString.Create(const Str: String);
+begin
+  Create(Str, False);
+end;
+
+constructor TDerPrintableString.Create(const Str: String; validate: Boolean);
+begin
+  Inherited Create();
+  if (Str = '') then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SStrNil);
+  end;
+  if (validate and (not IsPrintableString(Str))) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SIllegalCharacters);
+  end;
+
+  FStr := Str;
+end;
+
+procedure TDerPrintableString.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.PrintableString,
+    GetOctets());
+end;
+
+class function TDerPrintableString.GetInstance(const obj: TObject)
+  : IDerPrintableString;
+begin
+  if ((obj = Nil) or (obj is TDerPrintableString)) then
+  begin
+    result := obj as TDerPrintableString;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerPrintableString.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerPrintableString;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerPrintableString))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := TDerPrintableString.Create
+    (TAsn1OctetString.GetInstance(o as TAsn1Object).GetOctets());
+end;
+
+{ TDerSequenceGenerator }
+
+procedure TDerSequenceGenerator.AddObject(const obj: IAsn1Encodable);
+var
+  temp: TDerOutputStream;
+begin
+  temp := TDerOutputStream.Create(F_bOut);
+  try
+    temp.WriteObject(obj);
+  finally
+    temp.Free;
+  end;
+end;
+
+procedure TDerSequenceGenerator.Close;
+var
+  temp: TCryptoLibByteArray;
+begin
+  F_bOut.Position := 0;
+  System.SetLength(temp, F_bOut.Size);
+  F_bOut.Read(temp[0], F_bOut.Size);
+  WriteDerEncoded(TAsn1Tags.Constructed or TAsn1Tags.Sequence, temp);
+end;
+
+constructor TDerSequenceGenerator.Create(outStream: TStream);
+begin
+  Inherited Create(outStream);
+  F_bOut := TMemoryStream.Create();
+end;
+
+constructor TDerSequenceGenerator.Create(outStream: TStream; tagNo: Int32;
+  isExplicit: Boolean);
+begin
+  Inherited Create(outStream, tagNo, isExplicit);
+  F_bOut := TMemoryStream.Create();
+end;
+
+destructor TDerSequenceGenerator.Destroy;
+begin
+  F_bOut.Free;
+  inherited Destroy;
+end;
+
+function TDerSequenceGenerator.GetRawOutputStream: TStream;
+begin
+  result := F_bOut;
+end;
+
+{ TDerT61String }
+
+class function TDerT61String.GetEncoding: TEncoding;
+begin
+  result := TEncoding.GetEncoding('iso-8859-1');
+end;
+
+function TDerT61String.GetStr: String;
+begin
+  result := FStr;
+end;
+
+function TDerT61String.GetOctets: TCryptoLibByteArray;
+var
+  LEncoding: TEncoding;
+begin
+  LEncoding := TDerT61String.GetEncoding();
+  try
+    result := TConverters.ConvertStringToBytes(Str, LEncoding);
+  finally
+    LEncoding.Free;
+  end;
+end;
+
+function TDerT61String.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerT61String;
+begin
+
+  if (not Supports(asn1Object, IDerT61String, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := Str = other.Str;
+end;
+
+constructor TDerT61String.Create(const Str: TCryptoLibByteArray);
+var
+  LEncoding: TEncoding;
+begin
+  Inherited Create();
+  LEncoding := TDerT61String.GetEncoding();
+  try
+    Create(TConverters.ConvertBytesToString(Str, LEncoding));
+  finally
+    LEncoding.Free;
+  end;
+end;
+
+constructor TDerT61String.Create(const Str: String);
+begin
+  Inherited Create();
+  if (Str = '') then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SStrNil);
+  end;
+
+  FStr := Str;
+end;
+
+procedure TDerT61String.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.T61String, GetOctets());
+end;
+
+class function TDerT61String.GetInstance(const obj: TObject): IDerT61String;
+begin
+  if ((obj = Nil) or (obj is TDerT61String)) then
+  begin
+    result := obj as TDerT61String;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerT61String.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerT61String;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerT61String))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := TDerT61String.Create(TAsn1OctetString.GetInstance(o as TAsn1Object)
+    .GetOctets());
+end;
+
+function TDerT61String.GetString: String;
+begin
+  result := Str;
+end;
+
+{ TDerUniversalString }
+
+function TDerUniversalString.GetStr: TCryptoLibByteArray;
+begin
+  result := FStr;
+end;
+
+function TDerUniversalString.GetOctets: TCryptoLibByteArray;
+begin
+  result := System.Copy(Str);
+end;
+
+function TDerUniversalString.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerUniversalString;
+begin
+
+  if (not Supports(asn1Object, IDerUniversalString, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := TArrayUtils.AreEqual(Str, other.Str);
+end;
+
+constructor TDerUniversalString.Create(const Str: TCryptoLibByteArray);
+begin
+  Inherited Create();
+  if (Str = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SStrNil);
+  end;
+
+  FStr := Str;
+end;
+
+procedure TDerUniversalString.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.UniversalString, Str);
+end;
+
+class function TDerUniversalString.GetInstance(const obj: TObject)
+  : IDerUniversalString;
+begin
+  if ((obj = Nil) or (obj is TDerUniversalString)) then
+  begin
+    result := obj as TDerUniversalString;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerUniversalString.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerUniversalString;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerUniversalString))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := TDerUniversalString.Create
+    (TAsn1OctetString.GetInstance(o as TAsn1Object).GetOctets());
+end;
+
+function TDerUniversalString.GetString: String;
+var
+  buffer: TStringList;
+  I: Int32;
+  enc: TCryptoLibByteArray;
+  ubyte: UInt32;
+begin
+  buffer := TStringList.Create();
+  buffer.LineBreak := '';
+  enc := GetDerEncoded();
+  buffer.Add('#');
+  I := 0;
+  try
+    while I <> System.length(enc) do
+    begin
+      ubyte := enc[I];
+      buffer.Add(FTable[(ubyte shr 4) and $F]);
+      buffer.Add(FTable[enc[I] and $F]);
+      System.Inc(I);
+    end;
+    result := buffer.Text;
+  finally
+    buffer.Free;
+  end;
+end;
+
+{ TDerUtf8String }
+
+function TDerUtf8String.GetStr: String;
+begin
+  result := FStr;
+end;
+
+function TDerUtf8String.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerUtf8String;
+begin
+
+  if (not Supports(asn1Object, IDerUtf8String, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := Str = other.Str;
+end;
+
+constructor TDerUtf8String.Create(const Str: TCryptoLibByteArray);
+begin
+  Create(TConverters.ConvertBytesToString(Str, TEncoding.UTF8));
+end;
+
+constructor TDerUtf8String.Create(const Str: String);
+begin
+  Inherited Create();
+  if (Str = '') then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SStrNil);
+  end;
+
+  FStr := Str;
+end;
+
+procedure TDerUtf8String.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.Utf8String,
+    TConverters.ConvertStringToBytes(Str, TEncoding.UTF8));
+end;
+
+class function TDerUtf8String.GetInstance(const obj: TObject): IDerUtf8String;
+begin
+  if ((obj = Nil) or (obj is TDerUtf8String)) then
+  begin
+    result := obj as TDerUtf8String;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerUtf8String.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerUtf8String;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerUtf8String))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := TDerUtf8String.Create(TAsn1OctetString.GetInstance(o as TAsn1Object)
+    .GetOctets());
+end;
+
+function TDerUtf8String.GetString: String;
+begin
+  result := Str;
+end;
+
+{ TDerVideotexString }
+
+function TDerVideotexString.GetmString: TCryptoLibByteArray;
+begin
+  result := FmString;
+end;
+
+function TDerVideotexString.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerVideotexString;
+begin
+
+  if (not Supports(asn1Object, IDerVideotexString, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := TArrayUtils.AreEqual(mString, other.mString);
+end;
+
+function TDerVideotexString.Asn1GetHashCode: Int32;
+begin
+  result := TArrayUtils.GetArrayHashCode(mString);
+end;
+
+constructor TDerVideotexString.Create(const encoding: TCryptoLibByteArray);
+begin
+  Inherited Create();
+  FmString := System.Copy(encoding);
+end;
+
+procedure TDerVideotexString.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.VideotexString, mString);
+end;
+
+class function TDerVideotexString.GetInstance(const obj: TObject)
+  : IDerVideotexString;
+begin
+  if ((obj = Nil) or (obj is TDerVideotexString)) then
+  begin
+    result := obj as TDerVideotexString;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+class function TDerVideotexString.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerVideotexString;
+var
+  o: IAsn1Object;
+begin
+  o := obj.GetObject();
+
+  if ((isExplicit) or (Supports(o, IDerVideotexString))) then
+  begin
+    result := GetInstance(o as TAsn1Object);
+    Exit;
+  end;
+
+  result := TDerVideotexString.Create
+    (TAsn1OctetString.GetInstance(o as TAsn1Object).GetOctets());
+end;
+
+class function TDerVideotexString.GetInstance(const obj: TCryptoLibByteArray)
+  : IDerVideotexString;
+begin
+  try
+    result := FromByteArray(obj) as IDerVideotexString;
+  except
+    on e: Exception do
+    begin
+      raise EArgumentCryptoLibException.CreateResFmt(@SEncodingError,
+        [e.Message]);
+    end;
+
+  end;
+end;
+
+function TDerVideotexString.GetOctets: TCryptoLibByteArray;
+begin
+  result := System.Copy(mString);
+end;
+
+function TDerVideotexString.GetString: String;
+begin
+  result := TConverters.ConvertBytesToString(mString, TEncoding.ANSI)
+end;
+
+{ TDerVisibleString }
+
+function TDerVisibleString.GetStr: String;
+begin
+  result := FStr;
+end;
+
+function TDerVisibleString.GetOctets: TCryptoLibByteArray;
+begin
+  result := TConverters.ConvertStringToBytes(Str, TEncoding.ASCII);
+end;
+
+function TDerVisibleString.Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+var
+  other: IDerVisibleString;
+begin
+
+  if (not Supports(asn1Object, IDerVisibleString, other)) then
+  begin
+    result := False;
+    Exit;
+  end;
+
+  result := Str = other.Str;
+end;
+
+function TDerVisibleString.Asn1GetHashCode: Int32;
+begin
+  result := TStringUtils.GetStringHashCode(FStr);
+end;
+
+constructor TDerVisibleString.Create(const Str: TCryptoLibByteArray);
+begin
+  Create(TConverters.ConvertBytesToString(Str, TEncoding.ASCII));
+end;
+
+constructor TDerVisibleString.Create(const Str: String);
+begin
+  Inherited Create();
+  if (Str = '') then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SStrNil);
+  end;
+
+  FStr := Str;
+end;
+
+procedure TDerVisibleString.Encode(const derOut: TStream);
+begin
+  (derOut as TDerOutputStream).WriteEncoded(TAsn1Tags.VisibleString,
+    GetOctets());
+end;
+
+class function TDerVisibleString.GetInstance(const obj: TObject)
+  : IDerVisibleString;
+var
+  asn1OctetString: IAsn1OctetString;
+  asn1TaggedObject: IAsn1TaggedObject;
+begin
+  if ((obj = Nil) or (obj is TDerVisibleString)) then
+  begin
+    result := obj as TDerVisibleString;
+    Exit;
+  end;
+
+  if Supports(obj, IAsn1OctetString, asn1OctetString) then
+  begin
+    result := TDerVisibleString.Create(asn1OctetString.GetOctets());
+    Exit;
+  end;
+
+  if Supports(obj, IAsn1TaggedObject, asn1TaggedObject) then
+  begin
+    result := GetInstance(asn1TaggedObject.GetObject() as TAsn1Object);
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SIllegalObject,
+    [obj.ClassName]);
+end;
+
+{$IFNDEF _FIXINSIGHT_}
+
+class function TDerVisibleString.GetInstance(const obj: IAsn1TaggedObject;
+  isExplicit: Boolean): IDerVisibleString;
+begin
+  result := GetInstance(obj.GetObject() as TAsn1Object);
+end;
+{$ENDIF}
+
+function TDerVisibleString.GetString: String;
+begin
+  result := Str;
+end;
+
+end.

+ 103 - 0
src/libraries/cryptolib4pascal/ClpAsymmetricCipherKeyPair.pas

@@ -0,0 +1,103 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpAsymmetricCipherKeyPair;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpCryptoLibTypes,
+  ClpIAsymmetricKeyParameter,
+  ClpIAsymmetricCipherKeyPair;
+
+resourcestring
+  SExpectedPublicKey = 'Expected a Public Key "publicParameter"';
+  SExpectedPrivateKey = 'Expected a Private Key "privateParameter"';
+
+type
+  /// <summary>
+  /// a holding class for public/private parameter pairs <br />
+  /// </summary>
+  TAsymmetricCipherKeyPair = class sealed(TInterfacedObject,
+    IAsymmetricCipherKeyPair)
+
+  strict private
+  var
+    FpublicParameter, FprivateParameter: IAsymmetricKeyParameter;
+
+    function GetPrivate: IAsymmetricKeyParameter; inline;
+    function GetPublic: IAsymmetricKeyParameter; inline;
+
+  public
+
+    /// <summary>
+    /// basic constructor.
+    /// </summary>
+    /// <param name="publicParameter">
+    /// publicParam a public key parameters object.
+    /// </param>
+    /// <param name="privateParameter">
+    /// privateParam the corresponding private key parameters.
+    /// </param>
+    constructor Create(const publicParameter, privateParameter
+      : IAsymmetricKeyParameter);
+
+    /// <summary>
+    /// return the public key parameters.
+    /// </summary>
+    property &Public: IAsymmetricKeyParameter read GetPublic;
+
+    /// <summary>
+    /// return the private key parameters.
+    /// </summary>
+    property &Private: IAsymmetricKeyParameter read GetPrivate;
+
+  end;
+
+implementation
+
+{ TAsymmetricCipherKeyPair }
+
+constructor TAsymmetricCipherKeyPair.Create(const publicParameter,
+  privateParameter: IAsymmetricKeyParameter);
+begin
+  if (publicParameter.IsPrivate) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SExpectedPublicKey);
+  end;
+  if (not(privateParameter.IsPrivate)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SExpectedPrivateKey);
+  end;
+
+  FpublicParameter := publicParameter;
+  FprivateParameter := privateParameter;
+end;
+
+function TAsymmetricCipherKeyPair.GetPrivate: IAsymmetricKeyParameter;
+begin
+  Result := FprivateParameter;
+end;
+
+function TAsymmetricCipherKeyPair.GetPublic: IAsymmetricKeyParameter;
+begin
+  Result := FpublicParameter;
+end;
+
+end.

+ 87 - 0
src/libraries/cryptolib4pascal/ClpAsymmetricKeyParameter.pas

@@ -0,0 +1,87 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpAsymmetricKeyParameter;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpICipherParameters,
+  ClpIAsymmetricKeyParameter;
+
+type
+  TAsymmetricKeyParameter = class abstract(TInterfacedObject,
+    IAsymmetricKeyParameter, ICipherParameters)
+
+  strict private
+  var
+    FprivateKey: Boolean;
+
+  strict protected
+    function GetPrivateKey: Boolean; inline;
+    function GetIsPrivate: Boolean; inline;
+
+    constructor Create(privateKey: Boolean);
+
+  public
+    property IsPrivate: Boolean read GetIsPrivate;
+    property privateKey: Boolean read GetPrivateKey;
+    function Equals(const other: IAsymmetricKeyParameter): Boolean; reintroduce;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+  end;
+
+implementation
+
+{ TAsymmetricKeyParameter }
+
+constructor TAsymmetricKeyParameter.Create(privateKey: Boolean);
+begin
+  FprivateKey := privateKey;
+end;
+
+function TAsymmetricKeyParameter.Equals(const other
+  : IAsymmetricKeyParameter): Boolean;
+begin
+  if (other = Nil) then
+  begin
+    Result := false;
+    Exit;
+  end;
+  Result := FprivateKey = other.privateKey;
+end;
+
+function TAsymmetricKeyParameter.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  Result := Ord(FprivateKey);
+end;
+
+function TAsymmetricKeyParameter.GetIsPrivate: Boolean;
+begin
+  Result := FprivateKey;
+end;
+
+function TAsymmetricKeyParameter.GetPrivateKey: Boolean;
+begin
+  Result := FprivateKey;
+end;
+
+end.

+ 218 - 0
src/libraries/cryptolib4pascal/ClpBaseKdfBytesGenerator.pas

@@ -0,0 +1,218 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpBaseKdfBytesGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpIDigest,
+  ClpIKdfParameters,
+  ClpIIso18033KdfParameters,
+  ClpIDerivationFunction,
+  ClpIDerivationParameters,
+  ClpIBaseKdfBytesGenerator,
+  ClpConverters,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SOutputBufferTooSmall = 'Output Buffer too Small';
+  SOutputLengthTooLarge = 'Output Length too Large';
+  SKDFParameterNotFound = 'KDF Parameters Required For KDF Generator';
+
+type
+
+  /// <summary>
+  /// <para>
+  /// Basic KDF generator for derived keys and ivs as defined by IEEE
+  /// P1363a/ISO 18033
+  /// </para>
+  /// <para>
+  /// This implementation is based on ISO 18033/P1363a.
+  /// </para>
+  /// </summary>
+  TBaseKdfBytesGenerator = class(TInterfacedObject, IBaseKdfBytesGenerator,
+    IDerivationFunction)
+
+  strict protected
+  var
+    Fdigest: IDigest;
+    FcounterStart: Int32;
+    Fshared, Fiv: TCryptoLibByteArray;
+
+    function GetDigest(): IDigest; virtual;
+
+  public
+
+    /// <summary>
+    /// Construct a KDF Parameters generator.
+    /// </summary>
+    /// <param name="counterStart">
+    /// value of counter.
+    /// </param>
+    /// <param name="digest">
+    /// the digest to be used as the source of derived keys.
+    /// </param>
+    constructor Create(counterStart: Int32; const digest: IDigest);
+
+    procedure Init(const parameters: IDerivationParameters); virtual;
+
+    /// <summary>
+    /// return the underlying digest.
+    /// </summary>
+    property digest: IDigest read GetDigest;
+
+    /// <summary>
+    /// fill len bytes of the output buffer with bytes generated from the
+    /// derivation function.
+    /// </summary>
+    /// <exception cref="EArgumentCryptoLibException">
+    /// if the size of the request will cause an overflow.
+    /// </exception>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if the out buffer is too small.
+    /// </exception>
+    function GenerateBytes(const output: TCryptoLibByteArray;
+      outOff, length: Int32): Int32; virtual;
+
+  end;
+
+implementation
+
+{ TBaseKdfBytesGenerator }
+
+constructor TBaseKdfBytesGenerator.Create(counterStart: Int32;
+  const digest: IDigest);
+begin
+  Inherited Create();
+  FcounterStart := counterStart;
+  Fdigest := digest;
+end;
+
+function TBaseKdfBytesGenerator.GenerateBytes(const output: TCryptoLibByteArray;
+  outOff, length: Int32): Int32;
+var
+  outLen, cThreshold, i: Int32;
+  oBytes: Int64;
+  counterBase: UInt32;
+  dig, C: TCryptoLibByteArray;
+begin
+  if ((System.length(output) - length) < outOff) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooSmall);
+  end;
+
+  oBytes := length;
+  outLen := Fdigest.GetDigestSize;
+
+  //
+  // this is at odds with the standard implementation, the
+  // maximum value should be hBits * (2^32 - 1) where hBits
+  // is the digest output size in bits. We can't have an
+  // array with a long index at the moment...
+  //
+
+  if (oBytes > ((Int64(2) shl 32) - 1)) then
+  begin
+
+    raise EArgumentCryptoLibException.CreateRes(@SOutputLengthTooLarge);
+  end;
+
+  cThreshold := Int32((oBytes + outLen - 1) div outLen);
+
+  System.SetLength(dig, Fdigest.GetDigestSize);
+
+  System.SetLength(C, 4);
+
+  TConverters.ReadUInt32AsBytesBE(UInt32(FcounterStart), C, 0);
+
+  counterBase := UInt32(FcounterStart and (not $FF));
+
+  i := 0;
+  while i < cThreshold do
+  begin
+    Fdigest.BlockUpdate(Fshared, 0, System.length(Fshared));
+    Fdigest.BlockUpdate(C, 0, 4);
+
+    if (Fiv <> Nil) then
+    begin
+      Fdigest.BlockUpdate(Fiv, 0, System.length(Fiv));
+    end;
+
+    Fdigest.DoFinal(dig, 0);
+
+    if (length > outLen) then
+    begin
+      System.Move(dig[0], output[outOff], outLen * System.SizeOf(Byte));
+      outOff := outOff + outLen;
+      length := length - outLen;
+    end
+    else
+    begin
+      System.Move(dig[0], output[outOff], length * System.SizeOf(Byte));
+    end;
+
+    System.Inc(C[3]);
+    if (C[3] = 0) then
+
+    begin
+      counterBase := counterBase + $100;
+      TConverters.ReadUInt32AsBytesBE(counterBase, C, 0);
+    end;
+
+    System.Inc(i);
+  end;
+
+  Fdigest.Reset();
+
+  result := Int32(oBytes);
+end;
+
+function TBaseKdfBytesGenerator.GetDigest: IDigest;
+begin
+  result := Fdigest;
+end;
+
+procedure TBaseKdfBytesGenerator.Init(const parameters: IDerivationParameters);
+var
+  Lparameters: IDerivationParameters;
+  p1: IKdfParameters;
+  p2: IIso18033KdfParameters;
+begin
+  Lparameters := parameters;
+
+  if Supports(Lparameters, IKdfParameters, p1) then
+  begin
+    Fshared := p1.GetSharedSecret();
+    Fiv := p1.GetIV();
+  end
+  else if Supports(Lparameters, IIso18033KdfParameters, p2) then
+  begin
+    Fshared := p2.GetSeed();
+    Fiv := Nil;
+  end
+  else
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SKDFParameterNotFound);
+  end;
+
+end;
+
+end.

+ 5062 - 0
src/libraries/cryptolib4pascal/ClpBigInteger.pas

@@ -0,0 +1,5062 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpBigInteger;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  Math,
+  SysUtils,
+  StrUtils,
+  Generics.Collections,
+  ClpISecureRandom,
+  ClpIRandom,
+  ClpArrayUtils,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SDivisionByZero = 'Division by Zero Error';
+  SModulusPositive = 'Modulus must be Positive';
+  SNotRelativelyPrime = 'Numbers not Relatively Prime.';
+  SNegativeValue = 'Cannot be Called on Value < 0';
+  SNegativeExponent = 'Negative Exponent';
+  SResultTooLarge = 'Result too Large';
+  SNegativeBitPosition = 'Bit Position must not be Negative';
+  SInvalidBitAddress = 'Bit Address less than Zero';
+  SZeroLengthBigInteger = 'Zero length BigInteger';
+  SInvalidSign = 'Invalid Sign Value';
+  SNegativeSizeInBits = 'sizeInBits must be non-negative';
+  SInvalidBitLength = 'bitLength < 2';
+  SInvalidBase = 'Only bases 2, 8, 10, or 16 allowed';
+  SBadCharacterRadix8 = 'Bad Character in radix 8 string: %s';
+  SBadCharacterRadix2 = 'Bad Character in radix 2 string: %s';
+  SUnSupportedBase = 'Only bases 2, 8, 10, 16 are allowed';
+
+type
+{$SCOPEDENUMS ON}
+  TNumberStyles = (None = 0, AllowLeadingWhite = 1, AllowTrailingWhite = 2,
+    AllowLeadingSign = 4, Integer = 4 or 2 or 1, AllowTrailingSign = 8,
+    AllowParentheses = 16, AllowDecimalPoint = 32, AllowThousands = 64,
+    AllowExponent = 128, AllowCurrencySymbol = 256, AllowHexSpecifier = 512);
+{$SCOPEDENUMS OFF}
+
+type
+  TBigInteger = record
+
+  strict private
+
+  const
+
+    IMASK = Int64($FFFFFFFF);
+    UIMASK = UInt64($FFFFFFFF);
+    BitLengthTable: array [0 .. 255] of Byte = (0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4,
+      4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
+      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+      6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8,
+      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8);
+
+    /// <summary>
+    /// These are the threshold bit-lengths (of an exponent) where we
+    /// increase the window size. <br />They are calculated according to the
+    /// expected savings in multiplications. <br />Some squares will also be
+    /// saved on average, but we offset these against the extra storage
+    /// costs. <br />
+    /// </summary>
+    ExpWindowThresholds: array [0 .. 7] of Int32 = (7, 25, 81, 241, 673, 1793,
+      4609, Int32($7FFFFFFF));
+
+    // TODO Parse radix-2 64 bits at a time and radix-8 63 bits at a time
+    chunk2 = Int32(1);
+    chunk8 = Int32(1);
+    chunk10 = Int32(19);
+    chunk16 = Int32(16);
+
+    BitsPerByte = Int32(8);
+    BitsPerInt = Int32(32);
+    BytesPerInt = Int32(4);
+
+  var
+    // array of ints with [0] being the most significant
+    Fmagnitude: TCryptoLibInt32Array;
+    Fsign: Int32; // -1 means -ve; +1 means +ve; 0 means 0;
+    FnBits: Int32; // cache BitCount() value
+    FnBitLength: Int32; // cache BitLength() value
+    // -m^(-1) mod b, b = 2^32 (see Montgomery mult.), 0 when uninitialised
+    FmQuote: Int32;
+    FIsInitialized: Boolean;
+
+  class var
+
+    FZero, FOne, FTwo, FThree, FFour, FTen: TBigInteger;
+    // Each list has a product < 2^31
+    FprimeLists: TCryptoLibMatrixInt32Array;
+    FprimeProducts, FZeroMagnitude: TCryptoLibInt32Array;
+    FZeroEncoding: TCryptoLibByteArray;
+    FSMALL_CONSTANTS: TCryptoLibGenericArray<TBigInteger>;
+    Fradix2, Fradix2E, Fradix8, Fradix8E, Fradix10, Fradix10E, Fradix16,
+      Fradix16E: TBigInteger;
+    FRandomSource: ISecureRandom;
+
+    function GetBitLength: Int32; inline;
+    function GetBitCount: Int32; inline;
+    function GetInt32Value: Int32; inline;
+    function GetInt64Value: Int64; inline;
+    function GetIsInitialized: Boolean; inline;
+    function GetSignValue: Int32; inline;
+
+    function AddToMagnitude(const magToAdd: TCryptoLibInt32Array): TBigInteger;
+    function QuickPow2Check(): Boolean; inline;
+    /// <summary>
+    /// return z = x / y - done in place (z value preserved, x contains the *
+    /// remainder)
+    /// </summary>
+    function Divide(const x, y: TCryptoLibInt32Array)
+      : TCryptoLibInt32Array; overload;
+    function IsEqualMagnitude(const x: TBigInteger): Boolean;
+
+    function CheckProbablePrime(certainty: Int32; const random: IRandom;
+      randomlySelected: Boolean): Boolean;
+
+    function ModInversePow2(const m: TBigInteger): TBigInteger;
+
+    /// <summary>
+    /// Calculate mQuote = -m^(-1) mod b with b = 2^32 (32 = word size)
+    /// </summary>
+    function GetMQuote(): Int32; inline;
+
+    function Remainder(m: Int32): Int32; overload; inline;
+
+    /// <summary>
+    /// return x = x mod y - done in place (y value preserved)
+    /// </summary>
+    function Remainder(const x, y: TCryptoLibInt32Array)
+      : TCryptoLibInt32Array; overload;
+
+    function LastNBits(n: Int32): TCryptoLibInt32Array; inline;
+
+    function DivideWords(w: Int32): TBigInteger; inline;
+
+    function RemainderWords(w: Int32): TBigInteger; inline;
+
+    function ToByteArray(unsigned: Boolean): TCryptoLibByteArray; overload;
+
+    function FlipExistingBit(n: Int32): TBigInteger; inline;
+
+    function GetLowestSetBitMaskFirst(firstWordMask: Int32): Int32;
+
+    procedure ParseString(const str: String; radix: Int32);
+    procedure ParseBytes(const bytes: TCryptoLibByteArray;
+      offset, length: Int32);
+    procedure ParseBytesWithSign(sign: Int32; const bytes: TCryptoLibByteArray;
+      offset, length: Int32);
+
+    class function GetZero: TBigInteger; static; inline;
+    class function GetOne: TBigInteger; static; inline;
+    class function GetTwo: TBigInteger; static; inline;
+    class function GetThree: TBigInteger; static; inline;
+    class function GetFour: TBigInteger; static; inline;
+    class function GetTen: TBigInteger; static; inline;
+    class function GetprimeLists: TCryptoLibMatrixInt32Array; static; inline;
+    class function GetprimeProducts: TCryptoLibInt32Array; static; inline;
+    class function GetRandomSource: ISecureRandom; static; inline;
+
+    class function GetByteLength(nBits: Int32): Int32; static; inline;
+
+    class function MakeMagnitude(const bytes: TCryptoLibByteArray;
+      offset, length: Int32): TCryptoLibInt32Array; static;
+
+    /// <summary>
+    /// a = a + b - b preserved.
+    /// </summary>
+    class function AddMagnitudes(const a, b: TCryptoLibInt32Array)
+      : TCryptoLibInt32Array; static; inline;
+
+    class function CalcBitLength(sign, indx: Int32;
+      const mag: TCryptoLibInt32Array): Int32; static;
+
+    /// <summary>
+    /// unsigned comparison on two arrays - note the arrays may start with
+    /// leading zeros.
+    /// </summary>
+    class function CompareTo(xIndx: Int32; const x: TCryptoLibInt32Array;
+      yIndx: Int32; const y: TCryptoLibInt32Array): Int32; overload; static;
+
+    class function CompareNoLeadingZeroes(xIndx: Int32;
+      const x: TCryptoLibInt32Array; yIndx: Int32;
+      const y: TCryptoLibInt32Array): Int32; static;
+
+    class function ModInverse32(d: Int32): Int32; static; inline;
+
+    class function ModInverse64(d: Int64): Int64; static; inline;
+
+    /// <summary>
+    /// Calculate the numbers u1, u2, and u3 such that: <br />u1 * a + u2 * b
+    /// = u3 <br />where u3 is the greatest common divider of a and b. <br />
+    /// a and b using the extended Euclid algorithm (refer p. 323 of The Art
+    /// of Computer Programming vol 2, 2nd ed). <br />This also seems to have
+    /// the side effect of calculating some form of multiplicative inverse.
+    /// </summary>
+    /// <param name="a">
+    /// First number to calculate gcd for
+    /// </param>
+    /// <param name="b">
+    /// Second number to calculate gcd for
+    /// </param>
+    /// <param name="u1Out">
+    /// the return object for the u1 value
+    /// </param>
+    /// <returns>
+    /// The greatest common divisor of a and b
+    /// </returns>
+    class function ExtEuclid(const a, b: TBigInteger; out u1Out: TBigInteger)
+      : TBigInteger; static; inline;
+
+    class function ModPowBarrett(const b, e, m: TBigInteger)
+      : TBigInteger; static;
+
+    class function ReduceBarrett(const x: TBigInteger;
+      const m, mr, yu: TBigInteger): TBigInteger; static;
+
+    class function ModPowMonty(const b: TBigInteger; const e, m: TBigInteger;
+      convert: Boolean): TBigInteger; static;
+
+    class function GetWindowList(const mag: TCryptoLibInt32Array;
+      extraBits: Int32): TCryptoLibInt32Array; static;
+
+    class function CreateWindowEntry(mult, zeroes: Int32): Int32;
+      static; inline;
+
+    /// <returns>
+    /// w with w = x * x - w is assumed to have enough space.
+    /// </returns>
+    class function Square(const w, x: TCryptoLibInt32Array)
+      : TCryptoLibInt32Array; overload; static;
+
+    /// <returns>
+    /// x with x = y * z - x is assumed to have enough space.
+    /// </returns>
+    class function Multiply(const x, y, z: TCryptoLibInt32Array)
+      : TCryptoLibInt32Array; overload; static;
+
+    // mDash = -m^(-1) mod b
+    class procedure MontgomeryReduce(const x, m: TCryptoLibInt32Array;
+      mDash: UInt32); static;
+
+    // mDash = -m^(-1) mod b
+
+    /// <summary>
+    /// Montgomery multiplication: a = x * y * R^(-1) mod m <br />Based
+    /// algorithm 14.36 of Handbook of Applied Cryptography. <br />&lt;li&gt;
+    /// m, x, y should have length n &lt;/li&gt; <br />&lt;li&gt; a should
+    /// have length (n + 1) &lt;/li&gt; <br />&lt;li&gt; b = 2^32, R = b^n
+    /// &lt;/li&gt; <br />&lt;br/&gt; <br />The result is put in x <br />
+    /// &lt;br/&gt; <br />NOTE: the indices of x, y, m, a different in HAC
+    /// and in Java <br />
+    /// </summary>
+    class procedure MultiplyMonty(const a, x, y, m: TCryptoLibInt32Array;
+      mDash: UInt32; smallMontyModulus: Boolean); static;
+
+    // mDash = -m^(-1) mod b
+    class procedure SquareMonty(const a, x, m: TCryptoLibInt32Array;
+      mDash: UInt32; smallMontyModulus: Boolean); static;
+
+    class function MultiplyMontyNIsOne(x, y, m, mDash: UInt32): UInt32;
+      static; inline;
+
+    /// <summary>
+    /// do a left shift - this returns a new array.
+    /// </summary>
+    class function ShiftLeft(const mag: TCryptoLibInt32Array; n: Int32)
+      : TCryptoLibInt32Array; overload; static;
+
+    /// <summary>
+    /// do a right shift - this does it in place.
+    /// </summary>
+    class procedure ShiftRightInPlace(start: Int32;
+      const mag: TCryptoLibInt32Array; n: Int32); static;
+
+    /// <summary>
+    /// do a right shift by one - this does it in place.
+    /// </summary>
+    class procedure ShiftRightOneInPlace(start: Int32;
+      const mag: TCryptoLibInt32Array); static;
+
+    /// <summary>
+    /// returns x = x - y - we assume x is &gt;= y
+    /// </summary>
+    class function Subtract(xStart: Int32; const x: TCryptoLibInt32Array;
+      yStart: Int32; const y: TCryptoLibInt32Array): TCryptoLibInt32Array;
+      overload; static;
+
+    class function doSubBigLil(const bigMag, lilMag: TCryptoLibInt32Array)
+      : TCryptoLibInt32Array; static; inline;
+
+    class procedure AppendZeroExtendedString(var sl: TStringList;
+      const s: String; minLength: Int32); static; inline;
+
+    class procedure ToString(var sl: TStringList; radix: Int32;
+      var moduli: TList<TBigInteger>; scale: Int32; const pos: TBigInteger);
+      overload; static;
+
+    class function CreateUValueOf(value: UInt64): TBigInteger; static;
+    class function CreateValueOf(value: Int64): TBigInteger; static;
+
+    class function IntToBin(input: Int32): string; static;
+
+    class function IntToOctal(input: Int32): string; static;
+
+    constructor Create(signum: Int32; const mag: TCryptoLibInt32Array;
+      checkMag: Boolean); overload;
+
+    class procedure Boot(); static;
+    class constructor BigInteger();
+
+  public
+    property BitLength: Int32 read GetBitLength;
+    property BitCount: Int32 read GetBitCount;
+    property IsInitialized: Boolean read GetIsInitialized;
+    property Int32Value: Int32 read GetInt32Value;
+    property Int64Value: Int64 read GetInt64Value;
+    property SignValue: Int32 read GetSignValue;
+
+    class property Zero: TBigInteger read GetZero;
+    class property One: TBigInteger read GetOne;
+    class property Two: TBigInteger read GetTwo;
+    class property Three: TBigInteger read GetThree;
+    class property Four: TBigInteger read GetFour;
+    class property Ten: TBigInteger read GetTen;
+    class property primeLists: TCryptoLibMatrixInt32Array read GetprimeLists;
+    class property primeProducts: TCryptoLibInt32Array read GetprimeProducts;
+
+    class property RandomSource: ISecureRandom read GetRandomSource;
+
+    constructor Create(const value: String); overload;
+    constructor Create(const str: String; radix: Int32); overload;
+    constructor Create(const bytes: TCryptoLibByteArray); overload;
+    constructor Create(const bytes: TCryptoLibByteArray;
+      offset, length: Int32); overload;
+    constructor Create(sign: Int32; const bytes: TCryptoLibByteArray); overload;
+    constructor Create(sign: Int32; const bytes: TCryptoLibByteArray;
+      offset, length: Int32); overload;
+    constructor Create(sizeInBits: Int32; const random: IRandom); overload;
+    constructor Create(BitLength, certainty: Int32;
+      const random: IRandom); overload;
+
+    function Abs(): TBigInteger;
+    function Add(const value: TBigInteger): TBigInteger;
+    function Subtract(const n: TBigInteger): TBigInteger; overload;
+    function &And(const value: TBigInteger): TBigInteger;
+    function &Not(): TBigInteger;
+    function AndNot(const val: TBigInteger): TBigInteger;
+    function &Or(const value: TBigInteger): TBigInteger;
+    function &Xor(const value: TBigInteger): TBigInteger;
+
+    function CompareTo(const value: TBigInteger): Int32; overload;
+    function Divide(const val: TBigInteger): TBigInteger; overload;
+    function DivideAndRemainder(const val: TBigInteger)
+      : TCryptoLibGenericArray<TBigInteger>;
+    function Gcd(const value: TBigInteger): TBigInteger;
+    function Inc(): TBigInteger;
+
+    function RabinMillerTest(certainty: Int32; const random: IRandom)
+      : Boolean; overload;
+
+    function RabinMillerTest(certainty: Int32; const random: IRandom;
+      randomlySelected: Boolean): Boolean; overload;
+
+    /// <summary>
+    /// return whether or not a BigInteger is probably prime with a
+    /// probability of 1 - (1/2)**certainty. <br />&lt;p&gt;From Knuth Vol 2,
+    /// pg 395.&lt;/p&gt;
+    /// </summary>
+    function IsProbablePrime(certainty: Int32): Boolean; overload;
+    function IsProbablePrime(certainty: Int32; randomlySelected: Boolean)
+      : Boolean; overload;
+
+    function Max(const value: TBigInteger): TBigInteger;
+    function Min(const value: TBigInteger): TBigInteger;
+    function &Mod(const m: TBigInteger): TBigInteger;
+    function ModInverse(const m: TBigInteger): TBigInteger;
+    function ModPow(const e, m: TBigInteger): TBigInteger;
+
+    function Multiply(const val: TBigInteger): TBigInteger; overload;
+    function Square(): TBigInteger; overload;
+    function Negate(): TBigInteger;
+
+    function NextProbablePrime(): TBigInteger;
+
+    function Pow(exp: Int32): TBigInteger;
+
+    function Remainder(const n: TBigInteger): TBigInteger; overload;
+
+    function ShiftLeft(n: Int32): TBigInteger; overload;
+    function ShiftRight(n: Int32): TBigInteger;
+
+    function ToByteArray(): TCryptoLibByteArray; overload;
+    function ToByteArrayUnsigned(): TCryptoLibByteArray;
+
+    function TestBit(n: Int32): Boolean;
+    function SetBit(n: Int32): TBigInteger;
+    function ClearBit(n: Int32): TBigInteger;
+    function FlipBit(n: Int32): TBigInteger;
+
+    function GetLowestSetBit(): Int32;
+
+    function ToString(): String; overload;
+    function ToString(radix: Int32): String; overload;
+
+    function Equals(const other: TBigInteger): Boolean; inline;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+    inline;
+{$ENDIF DELPHI}
+    class function BitCnt(i: Int32): Int32; static;
+
+    /// <summary>
+    /// BitLen(value) is the number of bits in value.
+    /// </summary>
+    class function BitLen(w: Int32): Int32; static;
+    class function ProbablePrime(BitLength: Int32; const random: IRandom)
+      : TBigInteger; static;
+
+    class function ValueOf(value: Int64): TBigInteger; static;
+
+    class function Arbitrary(sizeInBits: Int32): TBigInteger; static;
+
+    class function Jacobi(const a, b: TBigInteger): Int32; static;
+
+  end;
+
+implementation
+
+uses
+  ClpSecureRandom; // included here to avoid circular dependency :)
+
+{ TBigInteger }
+
+class function TBigInteger.BitLen(w: Int32): Int32;
+var
+  v, t: UInt32;
+begin
+  v := UInt32(w);
+  t := v shr 24;
+  if (t <> 0) then
+  begin
+    Result := 24 + BitLengthTable[t];
+    Exit;
+  end;
+  t := v shr 16;
+  if (t <> 0) then
+  begin
+    Result := 16 + BitLengthTable[t];
+    Exit;
+  end;
+  t := v shr 8;
+  if (t <> 0) then
+  begin
+    Result := 8 + BitLengthTable[t];
+    Exit;
+  end;
+  Result := BitLengthTable[v];
+end;
+
+class function TBigInteger.CalcBitLength(sign, indx: Int32;
+  const mag: TCryptoLibInt32Array): Int32;
+var
+  BitLength, firstMag: Int32;
+begin
+  while True do
+
+  begin
+    if (indx >= System.length(mag)) then
+    begin
+      Result := 0;
+      Exit;
+    end;
+
+    if (mag[indx] <> 0) then
+    begin
+      break;
+    end;
+
+    System.Inc(indx);
+  end;
+
+  // bit length for everything after the first int
+  BitLength := 32 * ((System.length(mag) - indx) - 1);
+
+  // and determine bitlength of first int
+  firstMag := mag[indx];
+  BitLength := BitLength + BitLen(firstMag);
+
+  // Check for negative powers of two
+  if ((sign < 0) and ((firstMag and Int32(-firstMag)) = firstMag)) then
+  begin
+    repeat
+      System.Inc(indx);
+      if (indx >= System.length(mag)) then
+      begin
+        System.Dec(BitLength);
+        break;
+      end;
+    until (not(mag[indx] = 0));
+  end;
+
+  Result := BitLength;
+end;
+
+class function TBigInteger.GetZero: TBigInteger;
+begin
+  Result := FZero;
+end;
+
+class function TBigInteger.GetOne: TBigInteger;
+begin
+  Result := FOne;
+end;
+
+class function TBigInteger.GetTwo: TBigInteger;
+begin
+  Result := FTwo;
+end;
+
+class function TBigInteger.GetThree: TBigInteger;
+begin
+  Result := FThree;
+end;
+
+class function TBigInteger.GetFour: TBigInteger;
+begin
+  Result := FFour;
+end;
+
+class function TBigInteger.GetTen: TBigInteger;
+begin
+  Result := FTen;
+end;
+
+class function TBigInteger.GetprimeLists: TCryptoLibMatrixInt32Array;
+begin
+  Result := FprimeLists;
+end;
+
+class function TBigInteger.GetprimeProducts: TCryptoLibInt32Array;
+begin
+  Result := FprimeProducts;
+end;
+
+class function TBigInteger.GetRandomSource: ISecureRandom;
+begin
+  Result := FRandomSource;
+end;
+
+function TBigInteger.GetSignValue: Int32;
+begin
+  Result := Fsign;
+end;
+
+function TBigInteger.GetBitLength: Int32;
+begin
+  if (FnBitLength = -1) then
+  begin
+    if Fsign = 0 then
+    begin
+      FnBitLength := 0;
+    end
+    else
+    begin
+      FnBitLength := CalcBitLength(Fsign, 0, Fmagnitude);
+    end;
+
+  end;
+  Result := FnBitLength;
+end;
+
+function TBigInteger.GetInt32Value: Int32;
+var
+  n, v: Int32;
+begin
+  if (Fsign = 0) then
+  begin
+    Result := 0;
+    Exit;
+  end;
+
+  n := System.length(Fmagnitude);
+
+  v := Fmagnitude[n - 1];
+
+  if Fsign < 0 then
+  begin
+    Result := -v;
+  end
+  else
+  begin
+    Result := v;
+  end;
+end;
+
+function TBigInteger.GetInt64Value: Int64;
+var
+  n: Int32;
+  v: Int64;
+begin
+  if (Fsign = 0) then
+  begin
+    Result := 0;
+    Exit;
+  end;
+
+  n := System.length(Fmagnitude);
+
+  v := Fmagnitude[n - 1] and IMASK;
+  if (n > 1) then
+  begin
+    v := v or ((Fmagnitude[n - 2] and IMASK) shl 32);
+  end;
+
+  if Fsign < 0 then
+
+  begin
+    Result := -v;
+    Exit;
+  end
+  else
+  begin
+    Result := v;
+    Exit;
+  end;
+
+end;
+
+function TBigInteger.GetIsInitialized: Boolean;
+begin
+  Result := FIsInitialized;
+end;
+
+class function TBigInteger.BitCnt(i: Int32): Int32;
+var
+  u: UInt32;
+begin
+  u := UInt32(i);
+{$IFDEF FPC}
+  Result := Int32(PopCnt(u));
+{$ELSE}
+  u := u - ((u shr 1) and $55555555);
+  u := (u and $33333333) + ((u shr 2) and $33333333);
+  u := (u + (u shr 4)) and $0F0F0F0F;
+  u := u + (u shr 8);
+  u := u + (u shr 16);
+  u := u and $3F;
+  Result := Int32(u);
+{$ENDIF FPC}
+end;
+
+class function TBigInteger.CreateWindowEntry(mult, zeroes: Int32): Int32;
+begin
+  while ((mult and 1) = 0) do
+  begin
+    mult := mult shr 1;
+    System.Inc(zeroes);
+  end;
+
+  Result := mult or (zeroes shl 8);
+end;
+
+class function TBigInteger.GetByteLength(nBits: Int32): Int32;
+begin
+  Result := (nBits + BitsPerByte - 1) div BitsPerByte;
+end;
+
+function TBigInteger.LastNBits(n: Int32): TCryptoLibInt32Array;
+var
+  numWords, excessBits: Int32;
+begin
+  if (n < 1) then
+  begin
+    Result := FZeroMagnitude;
+    Exit;
+  end;
+
+  numWords := (n + BitsPerInt - 1) div BitsPerInt;
+  numWords := Math.Min(numWords, System.length(Fmagnitude));
+  System.SetLength(Result, numWords);
+  System.Move(Fmagnitude[System.length(Fmagnitude) - numWords], Result[0],
+    numWords * System.SizeOf(Int32));
+
+  excessBits := (numWords shl 5) - n;
+  if (excessBits > 0) then
+  begin
+    Result[0] := Result[0] and (Int32(System.High(UInt32) shr excessBits));
+  end;
+
+end;
+
+function TBigInteger.CompareTo(const value: TBigInteger): Int32;
+begin
+
+  if Fsign < value.Fsign then
+  begin
+    Result := -1;
+  end
+  else
+  begin
+    if Fsign > value.Fsign then
+    begin
+      Result := 1;
+    end
+    else
+    begin
+      if Fsign = 0 then
+      begin
+        Result := 0
+      end
+      else
+      begin
+        Result := Fsign * CompareNoLeadingZeroes(0, Fmagnitude, 0,
+          value.Fmagnitude);
+      end;
+    end;
+
+  end;
+
+end;
+
+class function TBigInteger.CreateValueOf(value: Int64): TBigInteger;
+begin
+  if (value < 0) then
+  begin
+    if (value = System.Low(Int64)) then
+    begin
+      Result := CreateValueOf(not value).&Not();
+      Exit;
+    end;
+
+    Result := CreateValueOf(-value).Negate();
+    Exit;
+  end;
+
+  Result := CreateUValueOf(UInt64(value));
+end;
+
+class function TBigInteger.CreateUValueOf(value: UInt64): TBigInteger;
+var
+  msw, lsw: Int32;
+begin
+  msw := Int32(value shr 32);
+  lsw := Int32(value);
+
+  if (msw <> 0) then
+  begin
+    Result := TBigInteger.Create(1, TCryptoLibInt32Array.Create(msw,
+      lsw), false);
+    Exit;
+  end;
+
+  if (lsw <> 0) then
+  begin
+    Result := TBigInteger.Create(1, TCryptoLibInt32Array.Create(lsw), false);
+    // Check for a power of two
+
+    if ((lsw and -lsw) = lsw) then
+    begin
+      Result.FnBits := 1;
+    end;
+    Exit;
+  end;
+
+  Result := Zero;
+end;
+
+class function TBigInteger.ValueOf(value: Int64): TBigInteger;
+begin
+  if ((value >= 0) and (value < System.length(FSMALL_CONSTANTS))) then
+  begin
+    Result := FSMALL_CONSTANTS[value];
+    Exit;
+  end;
+
+  Result := CreateValueOf(value);
+end;
+
+class procedure TBigInteger.Boot;
+var
+  i: UInt32;
+  primeList: TCryptoLibInt32Array;
+  product, j: Int32;
+begin
+
+  System.SetLength(FZeroEncoding, 0);
+  System.SetLength(FZeroMagnitude, 0);
+  FprimeLists := TCryptoLibMatrixInt32Array.Create
+    (TCryptoLibInt32Array.Create(3, 5, 7, 11, 13, 17, 19, 23),
+    TCryptoLibInt32Array.Create(29, 31, 37, 41, 43),
+    TCryptoLibInt32Array.Create(47, 53, 59, 61, 67),
+    TCryptoLibInt32Array.Create(71, 73, 79, 83), TCryptoLibInt32Array.Create(89,
+    97, 101, 103),
+
+    TCryptoLibInt32Array.Create(107, 109, 113, 127),
+    TCryptoLibInt32Array.Create(131, 137, 139, 149),
+    TCryptoLibInt32Array.Create(151, 157, 163, 167),
+    TCryptoLibInt32Array.Create(173, 179, 181, 191),
+    TCryptoLibInt32Array.Create(193, 197, 199, 211),
+
+    TCryptoLibInt32Array.Create(223, 227, 229), TCryptoLibInt32Array.Create(233,
+    239, 241), TCryptoLibInt32Array.Create(251, 257, 263),
+    TCryptoLibInt32Array.Create(269, 271, 277), TCryptoLibInt32Array.Create(281,
+    283, 293),
+
+    TCryptoLibInt32Array.Create(307, 311, 313), TCryptoLibInt32Array.Create(317,
+    331, 337), TCryptoLibInt32Array.Create(347, 349, 353),
+    TCryptoLibInt32Array.Create(359, 367, 373), TCryptoLibInt32Array.Create(379,
+    383, 389),
+
+    TCryptoLibInt32Array.Create(397, 401, 409), TCryptoLibInt32Array.Create(419,
+    421, 431), TCryptoLibInt32Array.Create(433, 439, 443),
+    TCryptoLibInt32Array.Create(449, 457, 461), TCryptoLibInt32Array.Create(463,
+    467, 479),
+
+    TCryptoLibInt32Array.Create(487, 491, 499), TCryptoLibInt32Array.Create(503,
+    509, 521), TCryptoLibInt32Array.Create(523, 541, 547),
+    TCryptoLibInt32Array.Create(557, 563, 569), TCryptoLibInt32Array.Create(571,
+    577, 587),
+
+    TCryptoLibInt32Array.Create(593, 599, 601), TCryptoLibInt32Array.Create(607,
+    613, 617), TCryptoLibInt32Array.Create(619, 631, 641),
+    TCryptoLibInt32Array.Create(643, 647, 653), TCryptoLibInt32Array.Create(659,
+    661, 673),
+
+    TCryptoLibInt32Array.Create(677, 683, 691), TCryptoLibInt32Array.Create(701,
+    709, 719), TCryptoLibInt32Array.Create(727, 733, 739),
+    TCryptoLibInt32Array.Create(743, 751, 757), TCryptoLibInt32Array.Create(761,
+    769, 773),
+
+    TCryptoLibInt32Array.Create(787, 797, 809), TCryptoLibInt32Array.Create(811,
+    821, 823), TCryptoLibInt32Array.Create(827, 829, 839),
+    TCryptoLibInt32Array.Create(853, 857, 859), TCryptoLibInt32Array.Create(863,
+    877, 881),
+
+    TCryptoLibInt32Array.Create(883, 887, 907), TCryptoLibInt32Array.Create(911,
+    919, 929), TCryptoLibInt32Array.Create(937, 941, 947),
+    TCryptoLibInt32Array.Create(953, 967, 971), TCryptoLibInt32Array.Create(977,
+    983, 991),
+
+    TCryptoLibInt32Array.Create(997, 1009, 1013),
+    TCryptoLibInt32Array.Create(1019, 1021, 1031),
+    TCryptoLibInt32Array.Create(1033, 1039, 1049),
+    TCryptoLibInt32Array.Create(1051, 1061, 1063),
+    TCryptoLibInt32Array.Create(1069, 1087, 1091),
+
+    TCryptoLibInt32Array.Create(1093, 1097, 1103),
+    TCryptoLibInt32Array.Create(1109, 1117, 1123),
+    TCryptoLibInt32Array.Create(1129, 1151, 1153),
+    TCryptoLibInt32Array.Create(1163, 1171, 1181),
+    TCryptoLibInt32Array.Create(1187, 1193, 1201),
+
+    TCryptoLibInt32Array.Create(1213, 1217, 1223),
+    TCryptoLibInt32Array.Create(1229, 1231, 1237),
+    TCryptoLibInt32Array.Create(1249, 1259, 1277),
+    TCryptoLibInt32Array.Create(1279, 1283, 1289));
+
+  // !!! Only Remove when we are able to move "ClpSecureRandom" to the
+  // interface uses section of this unit. !!!
+  TSecureRandom.Boot;
+
+  FRandomSource := TSecureRandom.Create();
+
+  FZero := TBigInteger.Create(0, FZeroMagnitude, false);
+  FZero.FnBits := 0;
+  FZero.FnBitLength := 0;
+
+  System.SetLength(FSMALL_CONSTANTS, 17);
+
+  FSMALL_CONSTANTS[0] := FZero;
+
+  i := 1;
+
+  while i < UInt32(System.length(FSMALL_CONSTANTS)) do
+  begin
+    FSMALL_CONSTANTS[i] := CreateUValueOf(i);
+    System.Inc(i);
+  end;
+
+  FOne := FSMALL_CONSTANTS[1];
+  FTwo := FSMALL_CONSTANTS[2];
+  FThree := FSMALL_CONSTANTS[3];
+  FFour := FSMALL_CONSTANTS[4];
+  FTen := FSMALL_CONSTANTS[10];
+
+  Fradix2 := ValueOf(2);
+  Fradix2E := Fradix2.Pow(chunk2);
+
+  Fradix8 := ValueOf(8);
+  Fradix8E := Fradix8.Pow(chunk8);
+
+  Fradix10 := ValueOf(10);
+
+  Fradix10E := Fradix10.Pow(chunk10);
+
+  Fradix16 := ValueOf(16);
+  Fradix16E := Fradix16.Pow(chunk16);
+
+  System.SetLength(FprimeProducts, System.length(primeLists));
+
+  for i := 0 to System.Pred(System.length(primeLists)) do
+  begin
+    primeList := primeLists[i];
+    product := primeList[0];
+    for j := 1 to System.Pred(System.length(primeList)) do
+    begin
+      product := product * primeList[j];
+    end;
+
+    FprimeProducts[i] := product;
+  end;
+
+end;
+
+function TBigInteger.QuickPow2Check: Boolean;
+begin
+  Result := (Fsign > 0) and (FnBits = 1);
+end;
+
+function TBigInteger.Negate: TBigInteger;
+begin
+  if (Fsign = 0) then
+  begin
+    Result := Self;
+    Exit;
+  end;
+
+  Result := TBigInteger.Create(-Fsign, Fmagnitude, false);
+end;
+
+class function TBigInteger.doSubBigLil(const bigMag,
+  lilMag: TCryptoLibInt32Array): TCryptoLibInt32Array;
+var
+  res: TCryptoLibInt32Array;
+begin
+  res := System.Copy(bigMag);
+
+  Result := Subtract(0, res, 0, lilMag);
+end;
+
+function TBigInteger.Inc: TBigInteger;
+begin
+  if (Fsign = 0) then
+  begin
+    Result := One;
+    Exit;
+  end;
+
+  if (Fsign < 0) then
+  begin
+    Result := TBigInteger.Create(-1, doSubBigLil(Fmagnitude,
+      One.Fmagnitude), True);
+    Exit;
+  end;
+
+  Result := AddToMagnitude(One.Fmagnitude);
+end;
+
+class function TBigInteger.IntToBin(input: Int32): string;
+var
+  bits: TCryptoLibCharArray;
+  i: Int32;
+begin
+
+  Result := '';
+
+  System.SetLength(bits, System.SizeOf(Int32) * 8);
+
+  i := 0;
+
+  while (input <> 0) do
+  begin
+    if (input and 1) = 1 then
+    begin
+      bits[i] := '1'
+    end
+    else
+    begin
+      bits[i] := '0';
+    end;
+    System.Inc(i);
+    input := input shr 1;
+  end;
+  System.SetString(Result, PChar(@bits[0]), i);
+
+  Result := ReverseString(Result);
+
+end;
+
+class function TBigInteger.IntToOctal(input: Int32): string;
+var
+  bits: TCryptoLibCharArray;
+  i: Int32;
+begin
+
+  Result := '';
+
+  System.SetLength(bits, System.SizeOf(Int32) * 8);
+
+  i := 0;
+
+  while (input <> 0) do
+  begin
+    case (input and 7) of
+      0:
+        bits[i] := '0';
+      1:
+        bits[i] := '1';
+      2:
+        bits[i] := '2';
+      3:
+        bits[i] := '3';
+      4:
+        bits[i] := '4';
+      5:
+        bits[i] := '5';
+      6:
+        bits[i] := '6';
+      7:
+        bits[i] := '7';
+    end;
+    System.Inc(i);
+    input := input shr 3;
+  end;
+
+  System.SetString(Result, PChar(@bits[0]), i);
+
+  Result := ReverseString(Result);
+
+end;
+
+function TBigInteger.&Not: TBigInteger;
+begin
+  Result := Inc().Negate();
+end;
+
+function TBigInteger.TestBit(n: Int32): Boolean;
+var
+  wordNum, word: Int32;
+begin
+  if (n < 0) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SNegativeBitPosition);
+  end;
+
+  if (Fsign < 0) then
+  begin
+    Result := (not &Not().TestBit(n));
+    Exit;
+  end;
+
+  wordNum := n div 32;
+  if (wordNum >= System.length(Fmagnitude)) then
+  begin
+    Result := false;
+    Exit;
+  end;
+
+  word := Fmagnitude[System.length(Fmagnitude) - 1 - wordNum];
+  Result := ((word shr (n and 31)) and 1) > 0;
+end;
+
+function TBigInteger.Abs: TBigInteger;
+begin
+  if Fsign >= 0 then
+  begin
+    Result := Self;
+  end
+  else
+  begin
+    Result := Negate();
+  end;
+end;
+
+function TBigInteger.Square: TBigInteger;
+var
+  resLength: Int32;
+  res: TCryptoLibInt32Array;
+begin
+  if (Fsign = 0) then
+  begin
+    Result := Zero;
+    Exit;
+  end;
+  if (QuickPow2Check()) then
+  begin
+    Result := ShiftLeft(Abs().BitLength - 1);
+    Exit;
+  end;
+  resLength := System.length(Fmagnitude) shl 1;
+
+  if (UInt32(Fmagnitude[0]) shr 16 = 0) then
+  begin
+    System.Dec(resLength);
+  end;
+  System.SetLength(res, resLength);
+  Square(res, Fmagnitude);
+  Result := TBigInteger.Create(1, res, false);
+end;
+
+function TBigInteger.Add(const value: TBigInteger): TBigInteger;
+begin
+  if (Fsign = 0) then
+  begin
+    Result := value;
+    Exit;
+  end;
+
+  if (Fsign <> value.Fsign) then
+  begin
+    if (value.Fsign = 0) then
+    begin
+      Result := Self;
+      Exit;
+    end;
+
+    if (value.Fsign < 0) then
+    begin
+      Result := Subtract(value.Negate());
+      Exit;
+    end;
+
+    Result := value.Subtract(Negate());
+    Exit;
+  end;
+
+  Result := AddToMagnitude(value.Fmagnitude);
+end;
+
+class function TBigInteger.AddMagnitudes(const a, b: TCryptoLibInt32Array)
+  : TCryptoLibInt32Array;
+var
+  tI, vI: Int32;
+  m: Int64;
+begin
+  tI := System.length(a) - 1;
+  vI := System.length(b) - 1;
+  m := 0;
+
+  while (vI >= 0) do
+  begin
+    m := m + (Int64(UInt32(a[tI])) + Int64(UInt32(b[vI])));
+    System.Dec(vI);
+    a[tI] := Int32(m);
+    System.Dec(tI);
+    m := Int64(UInt64(m shr 32));
+  end;
+
+  if (m <> 0) then
+  begin
+    while (tI >= 0) do
+    begin
+
+      a[tI] := a[tI] + 1;
+
+      if (a[tI] <> 0) then
+      begin
+        break;
+      end;
+      System.Dec(tI);
+    end;
+  end;
+
+  Result := a;
+end;
+
+function TBigInteger.AddToMagnitude(const magToAdd: TCryptoLibInt32Array)
+  : TBigInteger;
+var
+  big, small, bigCopy: TCryptoLibInt32Array;
+  limit: UInt32;
+  possibleOverflow: Boolean;
+begin
+  if (System.length(Fmagnitude) < System.length(magToAdd)) then
+  begin
+    big := magToAdd;
+    small := Fmagnitude;
+  end
+  else
+  begin
+    big := Fmagnitude;
+    small := magToAdd;
+  end;
+
+  // Conservatively avoid over-allocation when no overflow possible
+  limit := System.High(UInt32);
+  if (System.length(big) = System.length(small)) then
+  begin
+    limit := limit - UInt32(small[0]);
+  end;
+
+  possibleOverflow := UInt32(big[0]) >= limit;
+
+  if (possibleOverflow) then
+  begin
+    System.SetLength(bigCopy, System.length(big) + 1);
+    System.Move(big[0], bigCopy[1], System.length(big) * System.SizeOf(Int32));
+  end
+  else
+  begin
+    bigCopy := System.Copy(big);
+  end;
+
+  bigCopy := AddMagnitudes(bigCopy, small);
+
+  Result := TBigInteger.Create(Fsign, bigCopy, possibleOverflow);
+end;
+
+function TBigInteger.&And(const value: TBigInteger): TBigInteger;
+var
+  aMag, bMag, resultMag: TCryptoLibInt32Array;
+  resultNeg: Boolean;
+  resultLength, aStart, bStart, i, aWord, bWord: Int32;
+begin
+  if ((Fsign = 0) or (value.Fsign = 0)) then
+  begin
+    Result := Zero;
+    Exit;
+  end;
+
+  if Fsign > 0 then
+  begin
+    aMag := Fmagnitude;
+  end
+  else
+  begin
+    aMag := Add(One).Fmagnitude;
+  end;
+
+  if value.Fsign > 0 then
+  begin
+    bMag := value.Fmagnitude;
+  end
+  else
+  begin
+    bMag := value.Add(One).Fmagnitude;
+  end;
+
+  resultNeg := (Fsign < 0) and (value.Fsign < 0);
+  resultLength := Math.Max(System.length(aMag), System.length(bMag));
+
+  System.SetLength(resultMag, resultLength);
+
+  aStart := System.length(resultMag) - System.length(aMag);
+  bStart := System.length(resultMag) - System.length(bMag);
+
+  for i := 0 to System.Pred(System.length(resultMag)) do
+
+  begin
+
+    if i >= aStart then
+    begin
+      aWord := aMag[i - aStart];
+    end
+    else
+    begin
+      aWord := 0;
+    end;
+
+    if i >= bStart then
+    begin
+      bWord := bMag[i - bStart];
+    end
+    else
+    begin
+      bWord := 0;
+    end;
+
+    if (Fsign < 0) then
+    begin
+      aWord := not aWord;
+    end;
+
+    if (value.Fsign < 0) then
+    begin
+      bWord := not bWord;
+    end;
+
+    resultMag[i] := aWord and bWord;
+
+    if (resultNeg) then
+    begin
+      resultMag[i] := not resultMag[i];
+    end;
+  end;
+
+  Result := TBigInteger.Create(1, resultMag, True);
+
+  // TODO Optimise this case
+  if (resultNeg) then
+  begin
+    Result := Result.&Not();
+  end;
+
+end;
+
+function TBigInteger.AndNot(const val: TBigInteger): TBigInteger;
+begin
+  Result := &And(val.&Not());
+end;
+
+class procedure TBigInteger.AppendZeroExtendedString(var sl: TStringList;
+  const s: String; minLength: Int32);
+var
+  len: Int32;
+begin
+  len := System.length(s);
+  while len < minLength do
+  begin
+    sl.Add('0');
+    System.Inc(len);
+  end;
+  sl.Add(s);
+end;
+
+class procedure TBigInteger.ToString(var sl: TStringList; radix: Int32;
+  var moduli: TList<TBigInteger>; scale: Int32; const pos: TBigInteger);
+var
+  s: String;
+  qr: TCryptoLibGenericArray<TBigInteger>;
+begin
+  if (pos.BitLength < 64) then
+  begin
+    s := IntToStr(pos.Int64Value);
+    if ((sl.Count > 1) or ((sl.Count = 1) and (sl[0] <> '-'))) then
+    begin
+      AppendZeroExtendedString(sl, s, 1 shl scale);
+    end
+    else if (pos.SignValue <> 0) then
+    begin
+      sl.Append(s);
+    end;
+    Exit;
+  end;
+
+  System.Dec(scale);
+  qr := pos.DivideAndRemainder(moduli[scale]);
+
+  ToString(sl, radix, moduli, scale, qr[0]);
+  ToString(sl, radix, moduli, scale, qr[1]);
+end;
+
+class function TBigInteger.Arbitrary(sizeInBits: Int32): TBigInteger;
+begin
+  Result := TBigInteger.Create(sizeInBits, RandomSource);
+end;
+
+function TBigInteger.Remainder(m: Int32): Int32;
+var
+  acc, posVal: Int64;
+  &pos: Int32;
+begin
+{$IFDEF DEBUG}
+  System.Assert(m > 0);
+{$ENDIF DEBUG}
+  acc := 0;
+  for pos := 0 to System.Pred(System.length(Fmagnitude)) do
+  begin
+    posVal := UInt32(Fmagnitude[pos]);
+    acc := ((acc shl 32) or posVal) mod m;
+  end;
+
+  Result := Int32(acc);
+end;
+
+function TBigInteger.Remainder(const n: TBigInteger): TBigInteger;
+var
+  val, rem: Int32;
+  tempRes: TCryptoLibInt32Array;
+begin
+  if (n.Fsign = 0) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SDivisionByZero);
+  end;
+
+  if (Fsign = 0) then
+  begin
+    Result := Zero;
+    Exit;
+  end;
+
+  // For small values, use fast remainder method
+  if (System.length(n.Fmagnitude) = 1) then
+  begin
+    val := n.Fmagnitude[0];
+
+    if (val > 0) then
+    begin
+      if (val = 1) then
+      begin
+        Result := Zero;
+        Exit;
+      end;
+
+      // TODO Make this func work on uint, and handle val == 1?
+      rem := Remainder(val);
+
+      if rem = 0 then
+      begin
+        Result := Zero;
+        Exit;
+      end
+      else
+      begin
+        Result := TBigInteger.Create(Fsign,
+          TCryptoLibInt32Array.Create(rem), false);
+        Exit;
+      end;
+
+    end;
+  end;
+
+  if (CompareNoLeadingZeroes(0, Fmagnitude, 0, n.Fmagnitude) < 0) then
+  begin
+    Result := Self;
+    Exit;
+  end;
+
+  if (n.QuickPow2Check()) then // n is power of two
+  begin
+    // TODO Move before small values branch above?
+    tempRes := LastNBits(n.Abs().BitLength - 1);
+  end
+  else
+  begin
+    tempRes := System.Copy(Fmagnitude);
+    tempRes := Remainder(tempRes, n.Fmagnitude);
+  end;
+
+  Result := TBigInteger.Create(Fsign, tempRes, True);
+end;
+
+function TBigInteger.&Mod(const m: TBigInteger): TBigInteger;
+var
+  biggie: TBigInteger;
+begin
+  if (m.Fsign < 1) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SModulusPositive);
+  end;
+
+  biggie := Remainder(m);
+
+  if biggie.Fsign >= 0 then
+  begin
+    Result := biggie;
+  end
+  else
+  begin
+    Result := biggie.Add(m);
+  end;
+end;
+
+function TBigInteger.&Or(const value: TBigInteger): TBigInteger;
+var
+  aMag, bMag, resultMag: TCryptoLibInt32Array;
+  resultNeg: Boolean;
+  resultLength, aStart, bStart, i, aWord, bWord: Int32;
+begin
+  if (Fsign = 0) then
+  begin
+    Result := value;
+    Exit;
+  end;
+
+  if (value.Fsign = 0) then
+  begin
+    Result := Self;
+    Exit;
+  end;
+
+  if Fsign > 0 then
+  begin
+    aMag := Fmagnitude;
+  end
+  else
+  begin
+    aMag := Add(One).Fmagnitude;
+  end;
+
+  if value.Fsign > 0 then
+  begin
+    bMag := value.Fmagnitude;
+  end
+  else
+  begin
+    bMag := value.Add(One).Fmagnitude;
+  end;
+
+  resultNeg := (Fsign < 0) or (value.Fsign < 0);
+  resultLength := Math.Max(System.length(aMag), System.length(bMag));
+
+  System.SetLength(resultMag, resultLength);
+
+  aStart := System.length(resultMag) - System.length(aMag);
+  bStart := System.length(resultMag) - System.length(bMag);
+
+  for i := 0 to System.Pred(System.length(resultMag)) do
+
+  begin
+
+    if i >= aStart then
+    begin
+      aWord := aMag[i - aStart];
+    end
+    else
+    begin
+      aWord := 0;
+    end;
+
+    if i >= bStart then
+    begin
+      bWord := bMag[i - bStart];
+    end
+    else
+    begin
+      bWord := 0;
+    end;
+
+    if (Fsign < 0) then
+    begin
+      aWord := not aWord;
+    end;
+
+    if (value.Fsign < 0) then
+    begin
+      bWord := not bWord;
+    end;
+
+    resultMag[i] := aWord or bWord;
+
+    if (resultNeg) then
+    begin
+      resultMag[i] := not resultMag[i];
+    end;
+  end;
+
+  Result := TBigInteger.Create(1, resultMag, True);
+
+  // TODO Optimise this case
+  if (resultNeg) then
+  begin
+    Result := Result.&Not();
+  end;
+
+end;
+
+function TBigInteger.&Xor(const value: TBigInteger): TBigInteger;
+var
+  aMag, bMag, resultMag: TCryptoLibInt32Array;
+  resultNeg: Boolean;
+  resultLength, aStart, bStart, i, aWord, bWord: Int32;
+begin
+  if (Fsign = 0) then
+  begin
+    Result := value;
+    Exit;
+  end;
+
+  if (value.Fsign = 0) then
+  begin
+    Result := Self;
+    Exit;
+  end;
+
+  if Fsign > 0 then
+  begin
+    aMag := Fmagnitude;
+  end
+  else
+  begin
+    aMag := Add(One).Fmagnitude;
+  end;
+
+  if value.Fsign > 0 then
+  begin
+    bMag := value.Fmagnitude;
+  end
+  else
+  begin
+    bMag := value.Add(One).Fmagnitude;
+  end;
+  // TODO Can just replace with sign != value.sign?
+  resultNeg := ((Fsign < 0) and (value.Fsign >= 0)) or
+    ((Fsign >= 0) and (value.Fsign < 0));
+  resultLength := Math.Max(System.length(aMag), System.length(bMag));
+
+  System.SetLength(resultMag, resultLength);
+
+  aStart := System.length(resultMag) - System.length(aMag);
+  bStart := System.length(resultMag) - System.length(bMag);
+
+  for i := 0 to System.Pred(System.length(resultMag)) do
+
+  begin
+
+    if i >= aStart then
+    begin
+      aWord := aMag[i - aStart];
+    end
+    else
+    begin
+      aWord := 0;
+    end;
+
+    if i >= bStart then
+    begin
+      bWord := bMag[i - bStart];
+    end
+    else
+    begin
+      bWord := 0;
+    end;
+
+    if (Fsign < 0) then
+    begin
+      aWord := not aWord;
+    end;
+
+    if (value.Fsign < 0) then
+    begin
+      bWord := not bWord;
+    end;
+
+    resultMag[i] := aWord xor bWord;
+
+    if (resultNeg) then
+    begin
+      resultMag[i] := not resultMag[i];
+    end;
+  end;
+
+  Result := TBigInteger.Create(1, resultMag, True);
+
+  // TODO Optimise this case
+  if (resultNeg) then
+  begin
+    Result := Result.&Not();
+  end;
+
+end;
+
+class constructor TBigInteger.BigInteger;
+begin
+  TBigInteger.Boot;
+end;
+
+constructor TBigInteger.Create(const value: String);
+begin
+  ParseString(value, 10);
+end;
+
+constructor TBigInteger.Create(const str: String; radix: Int32);
+begin
+  ParseString(str, radix);
+end;
+
+constructor TBigInteger.Create(const bytes: TCryptoLibByteArray);
+begin
+  ParseBytes(bytes, 0, System.length(bytes));
+end;
+
+constructor TBigInteger.Create(sign: Int32; const bytes: TCryptoLibByteArray);
+begin
+  ParseBytesWithSign(sign, bytes, 0, System.length(bytes));
+end;
+
+constructor TBigInteger.Create(const bytes: TCryptoLibByteArray;
+  offset, length: Int32);
+begin
+  ParseBytes(bytes, offset, length);
+end;
+
+constructor TBigInteger.Create(sign: Int32; const bytes: TCryptoLibByteArray;
+  offset, length: Int32);
+begin
+  ParseBytesWithSign(sign, bytes, offset, length);
+end;
+
+constructor TBigInteger.Create(BitLength, certainty: Int32;
+  const random: IRandom);
+var
+  nBytes, xBits, j: Int32;
+  mask, lead: Byte;
+  b: TCryptoLibByteArray;
+begin
+  if (BitLength < 2) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SInvalidBitLength);
+  end;
+
+  Fsign := 1;
+  FnBits := -1;
+  FnBitLength := BitLength;
+  FmQuote := 0;
+  FIsInitialized := True;
+
+  if (BitLength = 2) then
+  begin
+    if (random.Next(2) = 0) then
+    begin
+      Fmagnitude := Two.Fmagnitude
+    end
+    else
+    begin
+      Fmagnitude := Three.Fmagnitude
+    end;
+    Exit;
+
+  end;
+
+  nBytes := GetByteLength(BitLength);
+  System.SetLength(b, nBytes);
+
+  xBits := (BitsPerByte * nBytes) - BitLength;
+  mask := Byte(UInt32(255) shr xBits);
+  lead := Byte(1 shl (7 - xBits));
+
+  while True do
+  begin
+    random.NextBytes(b);
+
+    // strip off any excess bits in the MSB
+    b[0] := b[0] and mask;
+
+    // ensure the leading bit is 1 (to meet the strength requirement)
+    b[0] := b[0] or lead;
+
+    // ensure the trailing bit is 1 (i.e. must be odd)
+    b[nBytes - 1] := b[nBytes - 1] or 1;
+
+    Fmagnitude := MakeMagnitude(b, 0, System.length(b));
+    FnBits := -1;
+    FmQuote := 0;
+
+    if (certainty < 1) then
+    begin
+      break;
+    end;
+
+    if (CheckProbablePrime(certainty, random, True)) then
+    begin
+      break;
+    end;
+
+    j := 1;
+
+    while j < (System.length(Fmagnitude) - 1) do
+
+    begin
+      Fmagnitude[j] := Fmagnitude[j] xor random.Next();
+
+      if (CheckProbablePrime(certainty, random, True)) then
+      begin
+        Exit;
+      end;
+      System.Inc(j);
+    end;
+
+  end;
+
+end;
+
+constructor TBigInteger.Create(sizeInBits: Int32; const random: IRandom);
+var
+  nBytes, xBits: Int32;
+  b: TCryptoLibByteArray;
+begin
+  if (sizeInBits < 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SNegativeSizeInBits);
+  end;
+
+  Fsign := -1;
+  FnBits := -1;
+  FnBitLength := -1;
+  FmQuote := 0;
+  FIsInitialized := True;
+
+  if (sizeInBits = 0) then
+  begin
+    Fsign := 0;
+    Fmagnitude := FZeroMagnitude;
+    Exit;
+  end;
+
+  nBytes := GetByteLength(sizeInBits);
+  System.SetLength(b, nBytes);
+  random.NextBytes(b);
+
+  // strip off any excess bits in the MSB
+  xBits := (BitsPerByte * nBytes) - sizeInBits;
+  b[0] := b[0] and Byte(UInt32(255) shr xBits);
+
+  Fmagnitude := MakeMagnitude(b, 0, System.length(b));
+
+  if System.length(Fmagnitude) < 1 then
+  begin
+    Fsign := 0;
+  end
+  else
+  begin
+    Fsign := 1;
+  end;
+
+end;
+
+constructor TBigInteger.Create(signum: Int32; const mag: TCryptoLibInt32Array;
+  checkMag: Boolean);
+var
+  i: Int32;
+begin
+
+  Fsign := -1;
+  FnBits := -1;
+  FnBitLength := -1;
+  FmQuote := 0;
+  FIsInitialized := True;
+
+  if (checkMag) then
+  begin
+    i := 0;
+    while ((i < System.length(mag)) and (mag[i] = 0)) do
+    begin
+      System.Inc(i);
+    end;
+
+    if (i = System.length(mag)) then
+    begin
+      Fsign := 0;
+      Fmagnitude := FZeroMagnitude;
+    end
+    else
+    begin
+      Fsign := signum;
+
+      if (i = 0) then
+      begin
+        Fmagnitude := mag;
+      end
+      else
+      begin
+        // strip leading 0 words
+        System.SetLength(Fmagnitude, System.length(mag) - i);
+        System.Move(mag[i], Fmagnitude[0], System.length(Fmagnitude) *
+          System.SizeOf(Int32));
+      end
+    end;
+  end
+  else
+  begin
+    Fsign := signum;
+    Fmagnitude := mag;
+  end;
+
+end;
+
+function TBigInteger.Equals(const other: TBigInteger): Boolean;
+begin
+  Result := (Fsign = other.Fsign) and IsEqualMagnitude(other);
+end;
+
+class function TBigInteger.ExtEuclid(const a, b: TBigInteger;
+  out u1Out: TBigInteger): TBigInteger;
+var
+  u1, v1, u3, v3, oldU1: TBigInteger;
+  q: TCryptoLibGenericArray<TBigInteger>;
+begin
+  u1 := One;
+  v1 := Zero;
+  u3 := a;
+  v3 := b;
+
+  if (v3.Fsign > 0) then
+  begin
+    while True do
+
+    begin
+      q := u3.DivideAndRemainder(v3);
+      u3 := v3;
+      v3 := q[1];
+
+      oldU1 := u1;
+      u1 := v1;
+
+      if (v3.Fsign <= 0) then
+      begin
+        break;
+      end;
+
+      v1 := oldU1.Subtract(v1.Multiply(q[0]));
+    end;
+  end;
+
+  u1Out := u1;
+
+  Result := u3;
+end;
+
+function TBigInteger.FlipExistingBit(n: Int32): TBigInteger;
+var
+  mag: TCryptoLibInt32Array;
+begin
+{$IFDEF DEBUG}
+  System.Assert(Fsign > 0);
+  System.Assert(n >= 0);
+  System.Assert(n < BitLength - 1);
+{$ENDIF DEBUG}
+  mag := System.Copy(Fmagnitude);
+  mag[System.length(mag) - 1 - (n shr 5)] :=
+    mag[System.length(mag) - 1 - (n shr 5)] xor (1 shl (n and 31));
+  // Flip bit
+  // mag[mag.Length - 1 - (n / 32)] ^= (1 << (n % 32));
+  Result := TBigInteger.Create(Fsign, mag, false);
+end;
+
+function TBigInteger.FlipBit(n: Int32): TBigInteger;
+begin
+  if (n < 0) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SInvalidBitAddress);
+  end;
+
+  // TODO Handle negative values and zero
+  if ((Fsign > 0) and (n < (BitLength - 1))) then
+  begin
+    Result := FlipExistingBit(n);
+    Exit;
+  end;
+
+  Result := &Xor(One.ShiftLeft(n));
+end;
+
+function TBigInteger.Gcd(const value: TBigInteger): TBigInteger;
+var
+  r, u, v: TBigInteger;
+begin
+  if (value.Fsign = 0) then
+  begin
+    Result := Abs();
+    Exit;
+  end;
+
+  if (Fsign = 0) then
+  begin
+    Result := value.Abs();
+    Exit;
+  end;
+
+  u := Self;
+  v := value;
+
+  while (v.Fsign <> 0) do
+  begin
+    r := u.&Mod(v);
+    u := v;
+    v := r;
+  end;
+
+  Result := u;
+end;
+
+function TBigInteger.GetBitCount: Int32;
+var
+  sum, i: Int32;
+begin
+  if (FnBits = -1) then
+  begin
+    if (Fsign < 0) then
+    begin
+      // TODO Optimise this case
+      FnBits := &Not().BitCount;
+    end
+    else
+    begin
+      sum := 0;
+      for i := 0 to System.Pred(System.length(Fmagnitude)) do
+      begin
+        sum := sum + BitCnt(Fmagnitude[i]);
+      end;
+      FnBits := sum;
+    end;
+  end;
+
+  Result := FnBits;
+end;
+
+function TBigInteger.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+
+var
+  hc: Int32;
+begin
+  hc := System.length(Fmagnitude);
+  if (System.length(Fmagnitude) > 0) then
+  begin
+    hc := hc xor Fmagnitude[0];
+
+    if (System.length(Fmagnitude) > 1) then
+    begin
+      hc := hc xor Fmagnitude[System.length(Fmagnitude) - 1];
+    end;
+  end;
+
+  if Fsign < 0 then
+  begin
+    Result := not hc;
+  end
+  else
+  begin
+    Result := hc;
+  end;
+
+end;
+
+function TBigInteger.GetLowestSetBit: Int32;
+begin
+  if (Fsign = 0) then
+  begin
+    Result := -1;
+    Exit;
+  end;
+
+  Result := GetLowestSetBitMaskFirst(-1);
+end;
+
+function TBigInteger.GetLowestSetBitMaskFirst(firstWordMask: Int32): Int32;
+var
+  w, offset: Int32;
+  word: UInt32;
+begin
+  w := System.length(Fmagnitude);
+  offset := 0;
+
+  System.Dec(w);
+  word := UInt32(Fmagnitude[w] and firstWordMask);
+{$IFDEF DEBUG}
+  System.Assert(Fmagnitude[0] <> 0);
+{$ENDIF DEBUG}
+  while (word = 0) do
+  begin
+    System.Dec(w);
+    word := UInt32(Fmagnitude[w]);
+    offset := offset + 32;
+  end;
+
+  while ((word and $FF) = 0) do
+  begin
+    word := word shr 8;
+    offset := offset + 8;
+  end;
+
+  while ((word and 1) = 0) do
+  begin
+    word := word shr 1;
+    System.Inc(offset);
+  end;
+
+  Result := offset;
+end;
+
+class function TBigInteger.ModInverse32(d: Int32): Int32;
+var
+  x: Int32;
+begin
+  // Newton's method with initial estimate "correct to 4 bits"
+{$IFDEF DEBUG}
+  System.Assert((d and 1) <> 0);
+{$ENDIF DEBUG}
+  x := d + (((d + 1) and 4) shl 1); // d.x == 1 mod 2**4
+{$IFDEF DEBUG}
+  System.Assert(((d * x) and 15) = 1);
+{$ENDIF DEBUG}
+  x := x * (2 - (d * x)); // d.x == 1 mod 2**8
+  x := x * (2 - (d * x)); // d.x == 1 mod 2**16
+  x := x * (2 - (d * x)); // d.x == 1 mod 2**32
+{$IFDEF DEBUG}
+  System.Assert(d * x = 1);
+{$ENDIF DEBUG}
+  Result := x;
+end;
+
+function TBigInteger.GetMQuote: Int32;
+var
+  d: Int32;
+begin
+  if (FmQuote <> 0) then
+  begin
+    Result := FmQuote; // already calculated
+    Exit;
+  end;
+
+{$IFDEF DEBUG}
+  System.Assert(Fsign > 0);
+{$ENDIF DEBUG}
+  d := Int32(Fmagnitude[System.length(Fmagnitude) - 1]);
+  d := -d;
+
+{$IFDEF DEBUG}
+  System.Assert((d and 1) <> 0);
+{$ENDIF DEBUG}
+  FmQuote := ModInverse32(d);
+  Result := FmQuote;
+end;
+
+function TBigInteger.IsEqualMagnitude(const x: TBigInteger): Boolean;
+var
+  i: Integer;
+  xMag: TCryptoLibInt32Array;
+begin
+  xMag := x.Fmagnitude;
+  if (System.length(Fmagnitude) <> System.length(xMag)) then
+  begin
+    Result := false;
+    Exit;
+  end;
+  for i := 0 to System.Pred(System.length(Fmagnitude)) do
+  begin
+    if (Fmagnitude[i] <> xMag[i]) then
+    begin
+      Result := false;
+      Exit;
+    end;
+  end;
+  Result := True;
+end;
+
+function TBigInteger.IsProbablePrime(certainty: Int32;
+  randomlySelected: Boolean): Boolean;
+var
+  n: TBigInteger;
+begin
+  if (certainty <= 0) then
+  begin
+    Result := True;
+    Exit;
+  end;
+
+  n := Abs();
+
+  if (not n.TestBit(0)) then
+  begin
+    Result := n.Equals(Two);
+    Exit;
+  end;
+
+  if (n.Equals(One)) then
+  begin
+    Result := false;
+    Exit;
+  end;
+
+  Result := n.CheckProbablePrime(certainty, RandomSource, randomlySelected);
+end;
+
+class function TBigInteger.Jacobi(const a, b: TBigInteger): Int32;
+var
+  totalS, e, bLsw, a1Lsw: Int32;
+  a1, La, Lb: TBigInteger;
+begin
+  La := a;
+  Lb := b;
+{$IFDEF DEBUG}
+  System.Assert(La.SignValue >= 0);
+  System.Assert(Lb.SignValue > 0);
+  System.Assert(Lb.TestBit(0));
+  System.Assert(La.CompareTo(Lb) < 0);
+{$ENDIF DEBUG}
+  totalS := 1;
+  while True do
+  begin
+    if (La.SignValue = 0) then
+    begin
+      Result := 0;
+      Exit;
+    end;
+
+    if (La.Equals(One)) then
+    begin
+      break;
+    end;
+
+    e := La.GetLowestSetBit();
+
+    bLsw := Lb.Fmagnitude[System.length(Lb.Fmagnitude) - 1];
+    if (((e and 1) <> 0) and (((bLsw and 7) = 3) or ((bLsw and 7) = 5))) then
+    begin
+      totalS := -totalS;
+    end;
+
+    if (La.BitLength = e + 1) then
+    begin
+      break;
+    end;
+    a1 := La.ShiftRight(e);
+
+    a1Lsw := a1.Fmagnitude[System.length(a1.Fmagnitude) - 1];
+    if (((bLsw and 3) = 3) and ((a1Lsw and 3) = 3)) then
+    begin
+      totalS := -totalS;
+    end;
+
+    La := Lb.Remainder(a1);
+    Lb := a1;
+  end;
+  Result := totalS;
+end;
+
+function TBigInteger.IsProbablePrime(certainty: Int32): Boolean;
+begin
+  Result := IsProbablePrime(certainty, false);
+end;
+
+class function TBigInteger.MakeMagnitude(const bytes: TCryptoLibByteArray;
+  offset, length: Int32): TCryptoLibInt32Array;
+var
+  endPoint, firstSignificant, nInts, bCount, v, magnitudeIndex, i: Int32;
+  mag: TCryptoLibInt32Array;
+begin
+  endPoint := offset + length;
+
+  // strip leading zeros
+  firstSignificant := offset;
+  while ((firstSignificant < endPoint) and (bytes[firstSignificant] = 0)) do
+  begin
+
+    System.Inc(firstSignificant);
+  end;
+
+  if (firstSignificant >= endPoint) then
+  begin
+    Result := FZeroMagnitude;
+    Exit;
+  end;
+
+  nInts := (endPoint - firstSignificant + 3) div BytesPerInt;
+  bCount := (endPoint - firstSignificant) mod BytesPerInt;
+  if (bCount = 0) then
+  begin
+    bCount := BytesPerInt;
+  end;
+
+  if (nInts < 1) then
+  begin
+    Result := FZeroMagnitude;
+    Exit;
+  end;
+
+  System.SetLength(mag, nInts);
+
+  v := 0;
+  magnitudeIndex := 0;
+
+  i := firstSignificant;
+  while i < endPoint do
+
+  begin
+    v := v shl 8;
+    v := v or (bytes[i] and $FF);
+    System.Dec(bCount);
+    if (bCount <= 0) then
+    begin
+      mag[magnitudeIndex] := v;
+      System.Inc(magnitudeIndex);
+      bCount := BytesPerInt;
+      v := 0;
+    end;
+    System.Inc(i);
+  end;
+
+  if (magnitudeIndex < System.length(mag)) then
+  begin
+    mag[magnitudeIndex] := v;
+  end;
+
+  Result := mag;
+end;
+
+function TBigInteger.Max(const value: TBigInteger): TBigInteger;
+begin
+  if CompareTo(value) > 0 then
+  begin
+    Result := Self;
+  end
+  else
+  begin
+    Result := value;
+  end;
+end;
+
+function TBigInteger.Min(const value: TBigInteger): TBigInteger;
+begin
+  if CompareTo(value) < 0 then
+  begin
+    Result := Self;
+  end
+  else
+  begin
+    Result := value;
+  end;
+end;
+
+function TBigInteger.ModInverse(const m: TBigInteger): TBigInteger;
+var
+  d, x, Gcd: TBigInteger;
+begin
+  if (m.Fsign < 1) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SModulusPositive);
+  end;
+
+  // TODO Too slow at the moment
+  // // "Fast Key Exchange with Elliptic Curve Systems" R.Schoeppel
+  // if (m.TestBit(0))
+  // {
+  // //The Almost Inverse Algorithm
+  // int k = 0;
+  // BigInteger B = One, C = Zero, F = this, G = m, tmp;
+  //
+  // for (;;)
+  // {
+  // // While F is even, do F=F/u, C=C*u, k=k+1.
+  // int zeroes = F.GetLowestSetBit();
+  // if (zeroes > 0)
+  // {
+  // F = F.ShiftRight(zeroes);
+  // C = C.ShiftLeft(zeroes);
+  // k += zeroes;
+  // }
+  //
+  // // If F = 1, then return B,k.
+  // if (F.Equals(One))
+  // {
+  // BigInteger half = m.Add(One).ShiftRight(1);
+  // BigInteger halfK = half.ModPow(BigInteger.ValueOf(k), m);
+  // return B.Multiply(halfK).Mod(m);
+  // }
+  //
+  // if (F.CompareTo(G) < 0)
+  // {
+  // tmp = G; G = F; F = tmp;
+  // tmp = B; B = C; C = tmp;
+  // }
+  //
+  // F = F.Add(G);
+  // B = B.Add(C);
+  // }
+  // }
+
+  if (m.QuickPow2Check()) then
+  begin
+    Result := ModInversePow2(m);
+    Exit;
+  end;
+
+  d := Remainder(m);
+  Gcd := ExtEuclid(d, m, x);
+
+  if (not Gcd.Equals(One)) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SNotRelativelyPrime);
+  end;
+
+  if (x.Fsign < 0) then
+  begin
+    x := x.Add(m);
+  end;
+
+  Result := x;
+end;
+
+class function TBigInteger.ModInverse64(d: Int64): Int64;
+var
+  x: Int64;
+begin
+  // Newton's method with initial estimate "correct to 4 bits"
+{$IFDEF DEBUG}
+  System.Assert((d and Int64(1)) <> 0);
+{$ENDIF DEBUG}
+  x := d + (((d + Int64(1)) and Int64(4)) shl 1); // d.x == 1 mod 2**4
+{$IFDEF DEBUG}
+  System.Assert(((d * x) and Int64(15)) = Int64(1));
+{$ENDIF DEBUG}
+  x := x * (2 - (d * x)); // d.x == 1 mod 2**8
+  x := x * (2 - (d * x)); // d.x == 1 mod 2**16
+  x := x * (2 - (d * x)); // d.x == 1 mod 2**32
+  x := x * (2 - (d * x)); // d.x == 1 mod 2**64
+{$IFDEF DEBUG}
+  System.Assert(d * x = Int64(1));
+{$ENDIF DEBUG}
+  Result := x;
+end;
+
+function TBigInteger.ModInversePow2(const m: TBigInteger): TBigInteger;
+var
+  Pow, bitsCorrect: Int32;
+  inv64: Int64;
+  x, d, t: TBigInteger;
+begin
+{$IFDEF DEBUG}
+  System.Assert(m.SignValue > 0);
+  System.Assert(m.BitCount = 1);
+{$ENDIF DEBUG}
+  if (not TestBit(0)) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SNotRelativelyPrime);
+  end;
+
+  Pow := m.BitLength - 1;
+
+  inv64 := ModInverse64(Int64Value);
+  if (Pow < 64) then
+  begin
+    inv64 := inv64 and ((Int64(1) shl Pow) - 1);
+  end;
+
+  x := TBigInteger.ValueOf(inv64);
+
+  if (Pow > 64) then
+  begin
+    d := Remainder(m);
+    bitsCorrect := 64;
+
+    repeat
+      t := x.Multiply(d).Remainder(m);
+      x := x.Multiply(Two.Subtract(t)).Remainder(m);
+      bitsCorrect := bitsCorrect shl 1;
+    until (not(bitsCorrect < Pow));
+  end;
+
+  if (x.Fsign < 0) then
+  begin
+    x := x.Add(m);
+  end;
+
+  Result := x;
+end;
+
+function TBigInteger.ModPow(const e, m: TBigInteger): TBigInteger;
+var
+  negExp: Boolean;
+  le: TBigInteger;
+begin
+  le := e;
+  if (m.Fsign < 1) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SModulusPositive);
+  end;
+
+  if (m.Equals(One)) then
+  begin
+    Result := Zero;
+    Exit;
+  end;
+
+  if (le.Fsign = 0) then
+  begin
+    Result := One;
+    Exit;
+  end;
+
+  if (Fsign = 0) then
+  begin
+    Result := Zero;
+    Exit;
+  end;
+
+  negExp := le.Fsign < 0;
+  if (negExp) then
+  begin
+    le := le.Negate();
+  end;
+
+  Result := &Mod(m);
+
+  if (not le.Equals(One)) then
+  begin
+    if ((m.Fmagnitude[System.length(m.Fmagnitude) - 1] and 1) = 0) then
+    begin
+      Result := ModPowBarrett(Result, le, m);
+    end
+    else
+    begin
+      Result := ModPowMonty(Result, le, m, True);
+
+    end;
+  end;
+
+  if (negExp) then
+  begin
+    Result := Result.ModInverse(m);
+  end;
+
+end;
+
+class function TBigInteger.ModPowBarrett(const b, e, m: TBigInteger)
+  : TBigInteger;
+var
+  k, extraBits, expLength, numPowers, i, window, mult, lastZeroes, windowPos,
+    bits, j: Int32;
+  mr, yu, b2, y: TBigInteger;
+  oddPowers: TCryptoLibGenericArray<TBigInteger>;
+  windowList: TCryptoLibInt32Array;
+begin
+  k := System.length(m.Fmagnitude);
+  mr := One.ShiftLeft((k + 1) shl 5);
+  yu := One.ShiftLeft(k shl 6).Divide(m);
+
+  // Sliding window from MSW to LSW
+  extraBits := 0;
+  expLength := e.BitLength;
+  while (expLength > ExpWindowThresholds[extraBits]) do
+  begin
+    System.Inc(extraBits);
+  end;
+
+  numPowers := 1 shl extraBits;
+  System.SetLength(oddPowers, numPowers);
+  oddPowers[0] := b;
+
+  b2 := ReduceBarrett(b.Square(), m, mr, yu);
+
+  for i := 1 to System.Pred(numPowers) do
+
+  begin
+    oddPowers[i] := ReduceBarrett(oddPowers[i - 1].Multiply(b2), m, mr, yu);
+  end;
+
+  windowList := GetWindowList(e.Fmagnitude, extraBits);
+{$IFDEF DEBUG}
+  System.Assert(System.length(windowList) > 0);
+{$ENDIF DEBUG}
+  window := windowList[0];
+  mult := window and $FF;
+  lastZeroes := window shr 8;
+
+  if (mult = 1) then
+  begin
+    y := b2;
+    System.Dec(lastZeroes);
+  end
+  else
+  begin
+    y := oddPowers[mult shr 1];
+  end;
+
+  windowPos := 1;
+  window := windowList[windowPos];
+  System.Inc(windowPos);
+  while (window <> -1) do
+  begin
+
+    mult := window and $FF;
+
+    bits := lastZeroes + BitLengthTable[mult];
+
+    j := 0;
+    while j < bits do
+    begin
+      y := ReduceBarrett(y.Square(), m, mr, yu);
+      System.Inc(j);
+    end;
+
+    y := ReduceBarrett(y.Multiply(oddPowers[mult shr 1]), m, mr, yu);
+
+    lastZeroes := window shr 8;
+
+    window := windowList[windowPos];
+    System.Inc(windowPos);
+  end;
+
+  i := 0;
+  while i < lastZeroes do
+  begin
+    y := ReduceBarrett(y.Square(), m, mr, yu);
+    System.Inc(i);
+  end;
+
+  Result := y;
+end;
+
+class function TBigInteger.ModPowMonty(const b: TBigInteger;
+  const e, m: TBigInteger; convert: Boolean): TBigInteger;
+var
+  n, powR, extraBits, expLength, numPowers, i, window, mult, lastZeroes,
+    windowPos, bits, j: Int32;
+  smallMontyModulus: Boolean;
+  mDash: UInt32;
+  yAccum, zVal, tmp, zSquared, windowList, yVal: TCryptoLibInt32Array;
+  oddPowers: TCryptoLibMatrixInt32Array;
+  Lb: TBigInteger;
+begin
+  Lb := b;
+  n := System.length(m.Fmagnitude);
+  powR := 32 * n;
+  smallMontyModulus := (m.BitLength + 2) <= powR;
+  mDash := UInt32(m.GetMQuote());
+
+  // tmp = this * R mod m
+  if (convert) then
+  begin
+    Lb := Lb.ShiftLeft(powR).Remainder(m);
+  end;
+
+  System.SetLength(yAccum, n + 1);
+
+  zVal := Lb.Fmagnitude;
+{$IFDEF DEBUG}
+  System.Assert(System.length(zVal) <= n);
+{$ENDIF DEBUG}
+  if (System.length(zVal) < n) then
+  begin
+    System.SetLength(tmp, n);
+    System.Move(zVal[0], tmp[n - System.length(zVal)],
+      System.length(zVal) * System.SizeOf(Int32));
+    zVal := tmp;
+  end;
+
+  // Sliding window from MSW to LSW
+
+  extraBits := 0;
+
+  // Filter the common case of small RSA exponents with few bits set
+  if ((System.length(e.Fmagnitude) > 1) or (e.BitCount > 2)) then
+  begin
+    expLength := e.BitLength;
+    while (expLength > ExpWindowThresholds[extraBits]) do
+    begin
+      System.Inc(extraBits);
+    end;
+  end;
+
+  numPowers := 1 shl extraBits;
+
+  System.SetLength(oddPowers, numPowers);
+  oddPowers[0] := zVal;
+
+  zSquared := System.Copy(zVal, 0, System.length(zVal));
+
+  SquareMonty(yAccum, zSquared, m.Fmagnitude, mDash, smallMontyModulus);
+
+  for i := 1 to System.Pred(numPowers) do
+
+  begin
+    oddPowers[i] := System.Copy(oddPowers[i - 1]);
+    MultiplyMonty(yAccum, oddPowers[i], zSquared, m.Fmagnitude, mDash,
+      smallMontyModulus);
+
+  end;
+
+  windowList := GetWindowList(e.Fmagnitude, extraBits);
+{$IFDEF DEBUG}
+  System.Assert(System.length(windowList) > 1);
+{$ENDIF DEBUG}
+  window := windowList[0];
+  mult := window and $FF;
+  lastZeroes := window shr 8;
+
+  if (mult = 1) then
+  begin
+    yVal := zSquared;
+    System.Dec(lastZeroes);
+  end
+  else
+  begin
+    yVal := System.Copy(oddPowers[mult shr 1]);
+  end;
+
+  windowPos := 1;
+  window := windowList[windowPos];
+  System.Inc(windowPos);
+  while (Int32(window) <> Int32(-1)) do
+  begin
+    mult := window and $FF;
+
+    bits := lastZeroes + BitLengthTable[mult];
+
+    j := 0;
+    while j < bits do
+    begin
+      SquareMonty(yAccum, yVal, m.Fmagnitude, mDash, smallMontyModulus);
+      System.Inc(j);
+    end;
+
+    MultiplyMonty(yAccum, yVal, oddPowers[mult shr 1], m.Fmagnitude, mDash,
+      smallMontyModulus);
+
+    lastZeroes := window shr 8;
+    window := windowList[windowPos];
+    System.Inc(windowPos);
+  end;
+
+  i := 0;
+  while i < lastZeroes do
+  begin
+    SquareMonty(yAccum, yVal, m.Fmagnitude, mDash, smallMontyModulus);
+    System.Inc(i);
+  end;
+
+  if (convert) then
+  begin
+    // Return y * R^(-1) mod m
+
+    MontgomeryReduce(yVal, m.Fmagnitude, mDash);
+
+  end
+  else if ((smallMontyModulus) and (CompareTo(0, yVal, 0, m.Fmagnitude) >= 0))
+  then
+  begin
+    Subtract(0, yVal, 0, m.Fmagnitude);
+  end;
+
+  Result := TBigInteger.Create(1, yVal, True);
+end;
+
+class function TBigInteger.Subtract(xStart: Int32;
+  const x: TCryptoLibInt32Array; yStart: Int32; const y: TCryptoLibInt32Array)
+  : TCryptoLibInt32Array;
+var
+  iT, iV, borrow: Int32;
+  m: Int64;
+begin
+{$IFDEF DEBUG}
+  System.Assert(yStart < System.length(y));
+  System.Assert(System.length(x) - xStart >= System.length(y) - yStart);
+{$ENDIF DEBUG}
+  iT := System.length(x);
+  iV := System.length(y);
+
+  borrow := 0;
+
+  repeat
+    System.Dec(iT);
+    System.Dec(iV);
+    m := (x[iT] and IMASK) - ((y[iV] and IMASK) + borrow);
+    // fixed precedence bug :)
+    x[iT] := Int32(m);
+
+    // borrow = (m < 0) ? -1 : 0;
+    borrow := Int32(m shr 63);
+  until (not(iV > yStart));
+
+  if (borrow <> 0) then
+  begin
+    System.Dec(iT);
+    x[iT] := x[iT] - 1;
+    while (x[iT] = -1) do
+    begin
+      System.Dec(iT);
+      x[iT] := x[iT] - 1;
+    end;
+
+  end;
+
+  Result := x;
+end;
+
+class procedure TBigInteger.MontgomeryReduce(const x, m: TCryptoLibInt32Array;
+  mDash: UInt32);
+var
+  n, i, j: Int32;
+  x0: UInt32;
+  t, carry: UInt64;
+begin
+  // NOTE: Not a general purpose reduction (which would allow x up to twice the bitlength of m)
+{$IFDEF DEBUG}
+  System.Assert(System.length(x) = System.length(m));
+{$ENDIF DEBUG}
+  n := System.length(m);
+
+  i := n - 1;
+
+  while i >= 0 do
+  begin
+
+    x0 := UInt32(x[n - 1]);
+    t := UInt32(UInt64(x0) * mDash);
+
+    carry := t * UInt32(m[n - 1]) + x0;
+
+{$IFDEF DEBUG}
+    System.Assert(UInt32(carry) = 0);
+{$ENDIF DEBUG}
+    carry := carry shr 32;
+
+    j := n - 2;
+
+    while j >= 0 do
+    begin
+      carry := carry + (t * UInt32(m[j]) + UInt32(x[j]));
+      x[j + 1] := Int32(carry);
+      carry := carry shr 32;
+      System.Dec(j);
+    end;
+
+    x[0] := Int32(carry);
+{$IFDEF DEBUG}
+    System.Assert(carry shr 32 = 0);
+{$ENDIF DEBUG}
+    System.Dec(i);
+  end;
+
+  if (CompareTo(0, x, 0, m) >= 0) then
+  begin
+    Subtract(0, x, 0, m);
+  end;
+end;
+
+class function TBigInteger.Multiply(const x, y, z: TCryptoLibInt32Array)
+  : TCryptoLibInt32Array;
+var
+  i, xBase, j: Int32;
+  a, val: Int64;
+begin
+  i := System.length(z);
+
+  if (i < 1) then
+  begin
+    Result := x;
+    Exit;
+  end;
+
+  xBase := System.length(x) - System.length(y);
+
+  repeat
+    System.Dec(i);
+    a := z[i] and IMASK;
+    val := 0;
+
+    if (a <> 0) then
+    begin
+      j := System.length(y) - 1;
+      while j >= 0 do
+      begin
+        val := val + (a * (y[j] and IMASK) + (x[xBase + j] and IMASK));
+
+        x[xBase + j] := Int32(val);
+
+        val := Int64(UInt64(val) shr 32);
+        System.Dec(j);
+      end;
+    end;
+
+    System.Dec(xBase);
+
+    if (xBase >= 0) then
+    begin
+      x[xBase] := Int32(val);
+    end
+    else
+    begin
+{$IFDEF DEBUG}
+      System.Assert(val = 0);
+{$ENDIF DEBUG}
+    end;
+
+  until (not(i > 0));
+
+  Result := x;
+end;
+
+function TBigInteger.Multiply(const val: TBigInteger): TBigInteger;
+var
+  resLength, resSign: Int32;
+  res: TCryptoLibInt32Array;
+begin
+  if (val.Equals(Self)) then
+  begin
+    Result := Square();
+    Exit;
+  end;
+
+  if ((Fsign and val.Fsign) = 0) then
+  begin
+    Result := Zero;
+    Exit;
+  end;
+
+  if (val.QuickPow2Check()) then // val is power of two
+  begin
+    Result := ShiftLeft(val.Abs().BitLength - 1);
+    if val.Fsign > 0 then
+    begin
+      Exit;
+    end
+    else
+    begin
+      Result := Result.Negate();
+      Exit;
+    end;
+
+  end;
+
+  if (QuickPow2Check()) then // this is power of two
+  begin
+
+    Result := val.ShiftLeft(Abs().BitLength - 1);
+    if Fsign > 0 then
+    begin
+      Exit;
+    end
+    else
+    begin
+      Result := Result.Negate();
+      Exit;
+    end;
+
+  end;
+
+  resLength := System.length(Fmagnitude) + System.length(val.Fmagnitude);
+  System.SetLength(res, resLength);
+
+  Multiply(res, Fmagnitude, val.Fmagnitude);
+
+  resSign := Fsign xor val.Fsign xor 1;
+  Result := TBigInteger.Create(resSign, res, True);
+end;
+
+class function TBigInteger.MultiplyMontyNIsOne(x, y, m, mDash: UInt32): UInt32;
+var
+  carry, um, prod2: UInt64;
+  t: UInt32;
+begin
+  carry := UInt64(UInt64(x) * y);
+  t := UInt32(UInt32(carry) * mDash);
+  um := m;
+  prod2 := um * t;
+  carry := carry + UInt32(prod2);
+{$IFDEF DEBUG}
+  System.Assert(UInt32(carry) = 0);
+{$ENDIF DEBUG}
+  carry := (carry shr 32) + (prod2 shr 32);
+  if (carry > um) then
+  begin
+    carry := carry - um;
+  end;
+{$IFDEF DEBUG}
+  System.Assert(carry < um);
+{$ENDIF DEBUG}
+  Result := UInt32(carry);
+end;
+
+class procedure TBigInteger.MultiplyMonty(const a, x, y,
+  m: TCryptoLibInt32Array; mDash: UInt32; smallMontyModulus: Boolean);
+var
+  n, aMax, j, i: Int32;
+  a0, y0: UInt32;
+  carry, t, prod1, prod2, xi: UInt64;
+begin
+  // mDash = -m^(-1) mod b
+
+  n := System.length(m);
+
+  if (n = 1) then
+  begin
+    x[0] := Int32(MultiplyMontyNIsOne(UInt32(x[0]), UInt32(y[0]),
+      UInt32(m[0]), mDash));
+    Exit;
+  end;
+  y0 := UInt32(y[n - 1]);
+
+  xi := UInt32(x[n - 1]);
+
+  carry := xi * y0;
+
+  t := UInt32(UInt64(UInt32(carry)) * mDash);
+
+  prod2 := t * UInt32(m[n - 1]);
+  carry := carry + UInt32(prod2);
+{$IFDEF DEBUG}
+  System.Assert(UInt32(carry) = 0);
+{$ENDIF DEBUG}
+  carry := (carry shr 32) + (prod2 shr 32);
+
+  j := n - 2;
+  while j >= 0 do
+  begin
+    prod1 := xi * UInt32(y[j]);
+    prod2 := t * UInt32(m[j]);
+
+    carry := carry + ((prod1 and UIMASK) + UInt32(prod2));
+    a[j + 2] := Int32(carry);
+    carry := (carry shr 32) + (prod1 shr 32) + (prod2 shr 32);
+    System.Dec(j);
+  end;
+
+  a[1] := Int32(carry);
+  aMax := Int32(carry shr 32);
+
+  i := n - 2;
+  while i >= 0 do
+  begin
+    a0 := UInt32(a[n]);
+    xi := UInt32(x[i]);
+
+    prod1 := xi * y0;
+    carry := (prod1 and UIMASK) + a0;
+    t := UInt32(UInt64(UInt32(carry)) * mDash);
+
+    prod2 := t * UInt32(m[n - 1]);
+    carry := carry + UInt32(prod2);
+{$IFDEF DEBUG}
+    System.Assert(UInt32(carry) = 0);
+{$ENDIF DEBUG}
+    carry := (carry shr 32) + (prod1 shr 32) + (prod2 shr 32);
+
+    j := n - 2;
+    while j >= 0 do
+    begin
+      prod1 := xi * UInt32(y[j]);
+      prod2 := t * UInt32(m[j]);
+
+      carry := carry + ((prod1 and UIMASK) + UInt32(prod2) + UInt32(a[j + 1]));
+      a[j + 2] := Int32(carry);
+      carry := (carry shr 32) + (prod1 shr 32) + (prod2 shr 32);
+      System.Dec(j);
+    end;
+
+    carry := carry + UInt32(aMax);
+    a[1] := Int32(carry);
+    aMax := Int32(carry shr 32);
+    System.Dec(i);
+  end;
+
+  a[0] := aMax;
+
+  if ((not smallMontyModulus) and (CompareTo(0, a, 0, m) >= 0)) then
+  begin
+    Subtract(0, a, 0, m);
+  end;
+
+  System.Move(a[1], x[0], n * System.SizeOf(Int32));
+
+end;
+
+function TBigInteger.NextProbablePrime: TBigInteger;
+var
+  n: TBigInteger;
+begin
+  if (Fsign < 0) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SNegativeValue);
+  end;
+
+  if (CompareTo(Two) < 0) then
+  begin
+    Result := Two;
+    Exit;
+  end;
+
+  n := Inc().SetBit(0);
+
+  while (not n.CheckProbablePrime(100, RandomSource, false)) do
+  begin
+    n := n.Add(Two);
+  end;
+
+  Result := n;
+end;
+
+procedure TBigInteger.ParseBytes(const bytes: TCryptoLibByteArray;
+  offset, length: Int32);
+var
+  endPoint, iBval, numBytes, index: Int32;
+  inverse: TCryptoLibByteArray;
+begin
+  if (length = 0) then
+  begin
+    raise EFormatCryptoLibException.CreateRes(@SZeroLengthBigInteger);
+  end;
+
+  Fsign := -1;
+  FnBits := -1;
+  FnBitLength := -1;
+  FmQuote := 0;
+  FIsInitialized := True;
+
+  // TODO Move this processing into MakeMagnitude (provide sign argument)
+  if (ShortInt(bytes[offset]) < 0) then
+  begin
+    Fsign := -1;
+
+    endPoint := offset + length;
+
+    iBval := offset;
+
+    // strip leading sign bytes
+    while (iBval < endPoint) and (ShortInt(bytes[iBval]) = -1) do
+    begin
+      System.Inc(iBval);
+    end;
+
+    if (iBval >= endPoint) then
+    begin
+      Fmagnitude := One.Fmagnitude;
+    end
+    else
+    begin
+      numBytes := endPoint - iBval;
+      System.SetLength(inverse, numBytes);
+
+      index := 0;
+      while (index < numBytes) do
+      begin
+        inverse[index] := Byte(not bytes[iBval]);
+        System.Inc(index);
+        System.Inc(iBval);
+      end;
+{$IFDEF DEBUG}
+      System.Assert(iBval = endPoint);
+{$ENDIF DEBUG}
+      System.Dec(index);
+      while (inverse[index] = System.High(Byte)) do
+      begin
+        inverse[index] := System.Low(Byte);
+        System.Dec(index);
+      end;
+
+      inverse[index] := Byte(inverse[index] + 1);
+
+      Fmagnitude := MakeMagnitude(inverse, 0, System.length(inverse));
+    end
+  end
+  else
+  begin
+    // strip leading zero bytes and return magnitude bytes
+    Fmagnitude := MakeMagnitude(bytes, offset, length);
+
+    if System.length(Fmagnitude) > 0 then
+    begin
+      Fsign := 1;
+    end
+    else
+    begin
+      Fsign := 0;
+    end;
+  end;
+
+end;
+
+procedure TBigInteger.ParseBytesWithSign(sign: Int32;
+  const bytes: TCryptoLibByteArray; offset, length: Int32);
+begin
+  begin
+    if ((sign < -1) or (sign > 1)) then
+    begin
+      raise EFormatCryptoLibException.CreateRes(@SInvalidSign);
+    end;
+
+    Fsign := -1;
+    FnBits := -1;
+    FnBitLength := -1;
+    FmQuote := 0;
+    FIsInitialized := True;
+
+    if (sign = 0) then
+    begin
+      Fsign := 0;
+      Fmagnitude := FZeroMagnitude;
+    end
+    else
+    begin
+      // copy bytes
+      Fmagnitude := MakeMagnitude(bytes, offset, length);
+      if System.length(Fmagnitude) < 1 then
+      begin
+        Fsign := 0;
+      end
+      else
+      begin
+        Fsign := sign;
+      end;
+
+    end;
+
+  end;
+end;
+
+procedure TBigInteger.ParseString(const str: String; radix: Int32);
+var
+  style: TNumberStyles;
+  chunk, index, Next, LowPoint, HighPoint: Int32;
+  r, rE, b, bi: TBigInteger;
+  dVal, s, temp: String;
+  i: UInt64;
+begin
+
+  if (System.length(str) = 0) then
+  begin
+    raise EFormatCryptoLibException.CreateRes(@SZeroLengthBigInteger);
+  end;
+
+  Fsign := -1;
+  FnBits := -1;
+  FnBitLength := -1;
+  FmQuote := 0;
+  FIsInitialized := True;
+
+  case radix of
+    2:
+      begin
+        // Is there anyway to restrict to binary digits?
+        style := TNumberStyles.Integer;
+        chunk := chunk2;
+        r := Fradix2;
+        rE := Fradix2E;
+      end;
+
+    8:
+      begin
+        // Is there anyway to restrict to octal digits?
+        style := TNumberStyles.Integer;
+        chunk := chunk8;
+        r := Fradix8;
+        rE := Fradix8E;
+      end;
+
+    10:
+      begin
+        // This style seems to handle spaces and minus sign already (our processing redundant?)
+        style := TNumberStyles.Integer;
+        chunk := chunk10;
+        r := Fradix10;
+        rE := Fradix10E;
+
+      end;
+
+    16:
+      begin
+        // TODO Should this be HexNumber?
+        style := TNumberStyles.AllowHexSpecifier;
+        chunk := chunk16;
+        r := Fradix16;
+        rE := Fradix16E;
+      end
+  else
+    begin
+      raise EFormatCryptoLibException.CreateRes(@SInvalidBase);
+    end;
+
+  end;
+
+{$IFDEF DELPHIXE3_UP}
+  LowPoint := System.Low(str);
+  HighPoint := System.High(str);
+{$ELSE}
+  LowPoint := 1;
+  HighPoint := System.length(str);
+{$ENDIF DELPHIXE3_UP}
+  index := LowPoint;
+  Fsign := 1;
+
+  if (str[LowPoint] = '-') then
+  begin
+    if (HighPoint = 1) then
+    begin
+      raise EFormatCryptoLibException.CreateRes(@SZeroLengthBigInteger);
+    end;
+
+    Fsign := -1;
+    index := LowPoint + 1;
+  end;
+
+  // strip leading zeros from the string str
+  while (index < (HighPoint + 1)) do
+  begin
+
+    dVal := str[index];
+
+    if (style = TNumberStyles.AllowHexSpecifier) then
+    begin
+      temp := '$' + dVal;
+    end
+    else
+    begin
+      temp := dVal;
+    end;
+
+    if (StrToInt(temp) = 0) then
+    begin
+      System.Inc(index);
+    end
+    else
+    begin
+      break;
+    end;
+  end;
+
+  if (index >= (HighPoint + 1)) then
+  begin
+    // zero value - we're done
+    Fsign := 0;
+    Fmagnitude := FZeroMagnitude;
+    Exit;
+  end;
+
+  /// ///
+  // could we work out the max number of ints required to store
+  // str.Length digits in the given base, then allocate that
+  // storage in one hit?, then Generate the magnitude in one hit too?
+  /// ///
+
+  b := Zero;
+
+  Next := index + chunk;
+
+  while (Next <= (HighPoint + 1)) do
+  begin
+    s := System.Copy(str, index, chunk);
+    if (style = TNumberStyles.AllowHexSpecifier) then
+    begin
+      temp := '$' + s;
+    end
+    else
+    begin
+      temp := s;
+    end;
+
+{$IFDEF FPC}
+    i := StrToQWord(temp);
+{$ELSE}
+    i := StrToUInt64(temp);
+{$ENDIF FPC}
+    bi := CreateUValueOf(i);
+
+    case (radix) of
+
+      2:
+        begin
+          // TODO Need this because we are parsing in radix 10 above
+          if (i >= 2) then
+          begin
+            raise EFormatCryptoLibException.CreateResFmt
+              (@SBadCharacterRadix2, [s]);
+          end;
+
+          // TODO Parse 64 bits at a time
+          b := b.ShiftLeft(1);
+
+        end;
+      8:
+        begin
+          // TODO Need this because we are parsing in radix 10 above
+          if (i >= 8) then
+          begin
+            raise EFormatCryptoLibException.CreateResFmt
+              (@SBadCharacterRadix8, [s]);
+          end;
+
+          // TODO Parse 63 bits at a time
+          b := b.ShiftLeft(3);
+
+        end;
+
+      16:
+        begin
+          b := b.ShiftLeft(64);
+
+        end
+    else
+      begin
+        b := b.Multiply(rE);
+      end;
+
+    end;
+
+    b := b.Add(bi);
+
+    index := Next;
+    Next := Next + chunk;
+
+  end;
+
+  if (index < System.length(str) + 1) then
+  begin
+    s := System.Copy(str, index, System.length(str) - (index - 1));
+
+    if (style = TNumberStyles.AllowHexSpecifier) then
+    begin
+      temp := '$' + s;
+    end
+    else
+    begin
+      temp := s;
+    end;
+
+{$IFDEF FPC}
+    i := StrToQWord(temp);
+{$ELSE}
+    i := StrToUInt64(temp);
+{$ENDIF FPC}
+    bi := CreateUValueOf(i);
+
+    if (b.Fsign > 0) then
+    begin
+      if (radix = 2) then
+      begin
+        // NB: Can't reach here since we are parsing one char at a time
+{$IFDEF DEBUG}
+        System.Assert(false);
+{$ENDIF DEBUG}
+        // TODO Parse all bits at once
+        // b = b.ShiftLeft(s.Length);
+      end
+      else if (radix = 8) then
+      begin
+        // NB: Can't reach here since we are parsing one char at a time
+{$IFDEF DEBUG}
+        System.Assert(false);
+{$ENDIF DEBUG}
+        // TODO Parse all bits at once
+        // b = b.ShiftLeft(s.Length * 3);
+      end
+      else if (radix = 16) then
+      begin
+        b := b.ShiftLeft(System.length(s) shl 2);
+      end
+      else
+      begin
+        b := b.Multiply(r.Pow(System.length(s)));
+      end;
+
+      b := b.Add(bi);
+    end
+    else
+    begin
+      b := bi;
+    end;
+  end;
+
+  Fmagnitude := b.Fmagnitude;
+
+end;
+
+function TBigInteger.Pow(exp: Int32): TBigInteger;
+var
+  powOf2: Int64;
+  y, z: TBigInteger;
+begin
+  if (exp <= 0) then
+  begin
+    if (exp < 0) then
+    begin
+      raise EArithmeticCryptoLibException.CreateRes(@SNegativeExponent);
+    end;
+
+    Result := One;
+    Exit;
+  end;
+
+  if (Fsign = 0) then
+  begin
+    Result := Self;
+    Exit;
+  end;
+
+  if (QuickPow2Check()) then
+  begin
+    powOf2 := Int64(exp) * (Int64(BitLength) - 1);
+    if (powOf2 > System.High(Int32)) then
+    begin
+      raise EArithmeticCryptoLibException.CreateRes(@SResultTooLarge);
+    end;
+    Result := One.ShiftLeft(Int32(powOf2));
+    Exit;
+  end;
+
+  y := One;
+  z := Self;
+
+  while True do
+  begin
+    if ((exp and $1) = 1) then
+    begin
+      y := y.Multiply(z);
+    end;
+    exp := exp shr 1;
+    if (exp = 0) then
+    begin
+      break;
+    end;
+    z := z.Multiply(z);
+  end;
+
+  Result := y;
+end;
+
+function TBigInteger.DivideWords(w: Int32): TBigInteger;
+var
+  n: Int32;
+  mag: TCryptoLibInt32Array;
+begin
+{$IFDEF DEBUG}
+  System.Assert(w >= 0);
+{$ENDIF DEBUG}
+  n := System.length(Fmagnitude);
+  if (w >= n) then
+  begin
+    Result := Zero;
+    Exit;
+  end;
+
+  System.SetLength(mag, n - w);
+  System.Move(Fmagnitude[0], mag[0], (n - w) * System.SizeOf(Int32));
+  Result := TBigInteger.Create(Fsign, mag, false);
+end;
+
+function TBigInteger.RemainderWords(w: Int32): TBigInteger;
+var
+  n: Int32;
+  mag: TCryptoLibInt32Array;
+begin
+{$IFDEF DEBUG}
+  System.Assert(w >= 0);
+{$ENDIF DEBUG}
+  n := System.length(Fmagnitude);
+  if (w >= n) then
+  begin
+    Result := Self;
+    Exit;
+  end;
+
+  System.SetLength(mag, w);
+  System.Move(Fmagnitude[n - w], mag[0], w * System.SizeOf(Int32));
+  Result := TBigInteger.Create(Fsign, mag, false);
+end;
+
+class function TBigInteger.ProbablePrime(BitLength: Int32;
+  const random: IRandom): TBigInteger;
+begin
+  Result := TBigInteger.Create(BitLength, 100, random);
+end;
+
+function TBigInteger.RabinMillerTest(certainty: Int32; const random: IRandom;
+  randomlySelected: Boolean): Boolean;
+var
+  bits, iterations, itersFor100Cert, s, j, shiftval: Int32;
+  n, r, montRadix, minusMontRadix, a, y: TBigInteger;
+begin
+  bits := BitLength;
+
+{$IFDEF DEBUG}
+  System.Assert(certainty > 0);
+  System.Assert(bits > 2);
+  System.Assert(TestBit(0));
+{$ENDIF DEBUG}
+  iterations := ((certainty - 1) shr 1) + 1;
+  if (randomlySelected) then
+  begin
+    if bits >= 1024 then
+    begin
+      itersFor100Cert := 4
+    end
+    else if bits >= 512 then
+    begin
+      itersFor100Cert := 8
+    end
+    else if bits >= 256 then
+    begin
+      itersFor100Cert := 16
+    end
+    else
+    begin
+      itersFor100Cert := 50
+    end;
+
+    if (certainty < 100) then
+    begin
+      iterations := Math.Min(itersFor100Cert, iterations);
+    end
+    else
+    begin
+      iterations := iterations - 50;
+      iterations := iterations + itersFor100Cert;
+    end;
+  end;
+
+  // let n = 1 + d . 2^s
+  n := Self;
+  shiftval := Int32(-1) shl 1; // -2
+  s := n.GetLowestSetBitMaskFirst(shiftval);
+{$IFDEF DEBUG}
+  System.Assert(s >= 1);
+{$ENDIF DEBUG}
+  r := n.ShiftRight(s);
+
+  // NOTE: Avoid conversion to/from Montgomery form and check for R/-R as result instead
+
+  montRadix := One.ShiftLeft(32 * System.length(n.Fmagnitude)).Remainder(n);
+  minusMontRadix := n.Subtract(montRadix);
+
+  repeat
+    repeat
+      a := TBigInteger.Create(n.BitLength, random);
+
+    until (not((a.Fsign = 0) or (a.CompareTo(n) >= 0) or
+      (a.IsEqualMagnitude(montRadix)) or (a.IsEqualMagnitude(minusMontRadix))));
+
+    y := ModPowMonty(a, r, n, false);
+
+    if (not y.Equals(montRadix)) then
+    begin
+      j := 0;
+      while (not y.Equals(minusMontRadix)) do
+      begin
+        System.Inc(j);
+        if (j = s) then
+        begin
+          Result := false;
+          Exit;
+        end;
+
+        y := ModPowMonty(y, Two, n, false);
+
+        if (y.Equals(montRadix)) then
+        begin
+          Result := false;
+          Exit;
+        end;
+      end;
+
+    end;
+    System.Dec(iterations);
+  until (not(iterations > 0));
+
+  Result := True;
+end;
+
+function TBigInteger.RabinMillerTest(certainty: Int32;
+  const random: IRandom): Boolean;
+begin
+  Result := RabinMillerTest(certainty, random, false);
+end;
+
+class function TBigInteger.ReduceBarrett(const x: TBigInteger;
+  const m, mr, yu: TBigInteger): TBigInteger;
+var
+  xLen, mLen, k: Int32;
+  q1, q2, q3, r1, r2, r3, lx: TBigInteger;
+begin
+  lx := x;
+  xLen := lx.BitLength;
+  mLen := m.BitLength;
+  if (xLen < mLen) then
+  begin
+    Result := lx;
+    Exit;
+  end;
+
+  if ((xLen - mLen) > 1) then
+  begin
+    k := System.length(m.Fmagnitude);
+
+    q1 := lx.DivideWords(k - 1);
+    q2 := q1.Multiply(yu); // TODO Only need partial multiplication here
+    q3 := q2.DivideWords(k + 1);
+
+    r1 := lx.RemainderWords(k + 1);
+    r2 := q3.Multiply(m); // TODO Only need partial multiplication here
+    r3 := r2.RemainderWords(k + 1);
+
+    lx := r1.Subtract(r3);
+    if (lx.Fsign < 0) then
+    begin
+      lx := lx.Add(mr);
+    end;
+  end;
+
+  while (lx.CompareTo(m) >= 0) do
+  begin
+    lx := lx.Subtract(m);
+  end;
+
+  Result := lx;
+end;
+
+function TBigInteger.Remainder(const x, y: TCryptoLibInt32Array)
+  : TCryptoLibInt32Array;
+var
+  xStart, yStart, xyCmp, yBitLength, xBitLength, shift, cBitLength, cStart,
+    len: Int32;
+  c: TCryptoLibInt32Array;
+  firstC, firstX: UInt32;
+begin
+  xStart := 0;
+  while ((xStart < System.length(x)) and (x[xStart] = 0)) do
+  begin
+    System.Inc(xStart);
+  end;
+
+  yStart := 0;
+
+  while ((yStart < System.length(y)) and (y[yStart] = 0)) do
+  begin
+    System.Inc(yStart);
+  end;
+
+{$IFDEF DEBUG}
+  System.Assert(yStart < System.length(y));
+{$ENDIF DEBUG}
+  xyCmp := CompareNoLeadingZeroes(xStart, x, yStart, y);
+
+  if (xyCmp > 0) then
+  begin
+    yBitLength := CalcBitLength(1, yStart, y);
+    xBitLength := CalcBitLength(1, xStart, x);
+    shift := xBitLength - yBitLength;
+
+    cStart := 0;
+    cBitLength := yBitLength;
+    if (shift > 0) then
+    begin
+
+      c := ShiftLeft(y, shift);
+      cBitLength := cBitLength + shift;
+{$IFDEF DEBUG}
+      System.Assert(c[0] <> 0);
+{$ENDIF DEBUG}
+    end
+    else
+    begin
+
+      len := System.length(y) - yStart;
+      System.SetLength(c, len);
+      System.Move(y[yStart], c[0], len * System.SizeOf(Int32));
+    end;
+
+    while True do
+    begin
+      if ((cBitLength < xBitLength) or (CompareNoLeadingZeroes(xStart, x,
+        cStart, c) >= 0)) then
+      begin
+        Subtract(xStart, x, cStart, c);
+
+        while (x[xStart] = 0) do
+        begin
+          System.Inc(xStart);
+          if (xStart = System.length(x)) then
+          begin
+            Result := x;
+            Exit;
+          end;
+        end;
+
+        // xBitLength = CalcBitLength(xStart, x);
+        xBitLength := 32 * (System.length(x) - xStart - 1) + BitLen(x[xStart]);
+
+        if (xBitLength <= yBitLength) then
+        begin
+          if (xBitLength < yBitLength) then
+          begin
+            Result := x;
+            Exit;
+          end;
+
+          xyCmp := CompareNoLeadingZeroes(xStart, x, yStart, y);
+
+          if (xyCmp <= 0) then
+          begin
+            break;
+          end;
+        end;
+      end;
+
+      shift := cBitLength - xBitLength;
+
+      // NB: The case where c[cStart] is 1-bit is harmless
+      if (shift = 1) then
+      begin
+        firstC := UInt32(c[cStart] shr 1);
+        firstX := UInt32(x[xStart]);
+        if (firstC > firstX) then
+        begin
+          System.Inc(shift);
+        end;
+      end;
+
+      if (shift < 2) then
+      begin
+        ShiftRightOneInPlace(cStart, c);
+        System.Dec(cBitLength);
+      end
+      else
+      begin
+        ShiftRightInPlace(cStart, c, shift);
+        cBitLength := cBitLength - shift;
+      end;
+
+      // cStart = c.Length - ((cBitLength + 31) / 32);
+      while (c[cStart] = 0) do
+      begin
+        System.Inc(cStart);
+      end;
+
+    end;
+  end;
+
+  if (xyCmp = 0) then
+  begin
+    TArrayUtils.Fill(x, xStart, System.length(x), Int32(0));
+  end;
+
+  Result := x;
+end;
+
+function TBigInteger.SetBit(n: Int32): TBigInteger;
+begin
+  if (n < 0) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SInvalidBitAddress);
+  end;
+
+  if (TestBit(n)) then
+  begin
+    Result := Self;
+    Exit;
+  end;
+
+  // TODO Handle negative values and zero
+  if ((Fsign > 0) and (n < (BitLength - 1))) then
+  begin
+    Result := FlipExistingBit(n);
+    Exit;
+  end;
+
+  Result := &Or(One.ShiftLeft(n));
+end;
+
+function TBigInteger.ShiftLeft(n: Int32): TBigInteger;
+begin
+  if ((Fsign = 0) or (System.length(Fmagnitude) = 0)) then
+  begin
+    Result := Zero;
+    Exit;
+  end;
+
+  if (n = 0) then
+  begin
+    Result := Self;
+    Exit;
+  end;
+
+  if (n < 0) then
+  begin
+    Result := ShiftRight(-n);
+    Exit;
+  end;
+
+  Result := TBigInteger.Create(Fsign, ShiftLeft(Fmagnitude, n), True);
+
+  // if (FnBits <> -1) then
+  if (BitCount <> -1) then
+  begin
+    if Fsign > 0 then
+    begin
+      Result.FnBits := FnBits;
+    end
+    else
+    begin
+      Result.FnBits := FnBits + n;
+    end;
+
+  end;
+
+  if (FnBitLength <> -1) then
+  begin
+    Result.FnBitLength := FnBitLength + n;
+  end;
+
+end;
+
+class function TBigInteger.ShiftLeft(const mag: TCryptoLibInt32Array; n: Int32)
+  : TCryptoLibInt32Array;
+var
+  nInts, nBits, magLen, i, nBits2, highBits, m, j, Next: Int32;
+  newMag: TCryptoLibInt32Array;
+
+begin
+  nInts := Int32(UInt32(n) shr 5);
+  nBits := n and $1F;
+  magLen := System.length(mag);
+
+  if (nBits = 0) then
+  begin
+    System.SetLength(newMag, magLen + nInts);
+    System.Move(mag[0], newMag[0], System.length(mag) * System.SizeOf(Int32));
+  end
+  else
+  begin
+    i := 0;
+    nBits2 := 32 - nBits;
+    highBits := Int32(UInt32(mag[0]) shr nBits2);
+
+    if (highBits <> 0) then
+    begin
+      System.SetLength(newMag, magLen + nInts + 1);
+
+      newMag[i] := highBits;
+      System.Inc(i);
+    end
+    else
+    begin
+      System.SetLength(newMag, magLen + nInts);
+
+    end;
+
+    m := mag[0];
+
+    j := 0;
+
+    while (j < (magLen - 1)) do
+    begin
+      Next := mag[j + 1];
+
+      newMag[i] := (m shl nBits) or Int32(UInt32(Next) shr nBits2);
+      System.Inc(i);
+      m := Next;
+      System.Inc(j);
+    end;
+
+    newMag[i] := mag[magLen - 1] shl nBits;
+  end;
+
+  Result := newMag;
+end;
+
+function TBigInteger.ShiftRight(n: Int32): TBigInteger;
+var
+  resultLength, numInts, numBits, numBits2, magPos, i: Int32;
+  res: TCryptoLibInt32Array;
+begin
+  if (n = 0) then
+  begin
+    Result := Self;
+    Exit;
+  end;
+
+  if (n < 0) then
+  begin
+    Result := ShiftLeft(-n);
+    Exit;
+  end;
+
+  if (n >= BitLength) then
+  begin
+    if Fsign < 0 then
+    begin
+      Result := One.Negate();
+      Exit;
+    end
+    else
+    begin
+      Result := Zero;
+      Exit;
+    end;
+
+  end;
+
+  // int[] res = (int[]) this.magnitude.Clone();
+  //
+  // ShiftRightInPlace(0, res, n);
+  //
+  // return new BigInteger(this.sign, res, true);
+
+  resultLength := (BitLength - n + 31) shr 5;
+  System.SetLength(res, resultLength);
+
+  numInts := n shr 5;
+  numBits := n and 31;
+
+  if (numBits = 0) then
+  begin
+    System.Move(Fmagnitude[0], res[0], System.length(res) *
+      System.SizeOf(Int32));
+  end
+  else
+  begin
+    numBits2 := 32 - numBits;
+
+    magPos := System.length(Fmagnitude) - 1 - numInts;
+
+    i := resultLength - 1;
+    while i >= 0 do
+    begin
+      res[i] := Int32(UInt32(Fmagnitude[magPos]) shr numBits);
+      System.Dec(magPos);
+
+      if (magPos >= 0) then
+      begin
+        res[i] := res[i] or (Fmagnitude[magPos] shl numBits2);
+      end;
+      System.Dec(i);
+    end;
+
+  end;
+
+{$IFDEF DEBUG}
+  System.Assert(res[0] <> 0);
+{$ENDIF DEBUG}
+  Result := TBigInteger.Create(Fsign, res, false);
+end;
+
+class procedure TBigInteger.ShiftRightInPlace(start: Int32;
+  const mag: TCryptoLibInt32Array; n: Int32);
+var
+  nInts, nBits, magEnd, delta, i, nBits2, m, Next: Int32;
+begin
+  nInts := Int32(UInt32(n) shr 5) + start;
+  nBits := n and $1F;
+  magEnd := System.length(mag) - 1;
+
+  if (nInts <> start) then
+  begin
+    delta := (nInts - start);
+
+    i := magEnd;
+    while i >= nInts do
+    begin
+      mag[i] := mag[i - delta];
+      System.Dec(i);
+    end;
+
+    i := nInts - 1;
+    while i >= start do
+    begin
+      mag[i] := 0;
+      System.Dec(i);
+    end;
+
+  end;
+
+  if (nBits <> 0) then
+  begin
+    nBits2 := 32 - nBits;
+    m := mag[magEnd];
+
+    i := magEnd;
+    while i > nInts do
+    begin
+      Next := mag[i - 1];
+
+      mag[i] := Int32(UInt32(m) shr nBits) or (Next shl nBits2);
+      m := Next;
+      System.Dec(i);
+    end;
+
+    mag[nInts] := Int32(UInt32(mag[nInts]) shr nBits);
+  end;
+end;
+
+class procedure TBigInteger.ShiftRightOneInPlace(start: Int32;
+  const mag: TCryptoLibInt32Array);
+var
+  i, m, Next: Int32;
+begin
+  i := System.length(mag);
+  m := mag[i - 1];
+
+  System.Dec(i);
+  while (i > start) do
+  begin
+    Next := mag[i - 1];
+    mag[i] := (Int32(UInt32(m) shr 1)) or (Next shl 31);
+    m := Next;
+    System.Dec(i);
+  end;
+
+  mag[start] := Int32(UInt32(mag[start]) shr 1);
+end;
+
+class function TBigInteger.Square(const w, x: TCryptoLibInt32Array)
+  : TCryptoLibInt32Array;
+var
+  c, v, prod: UInt64;
+  wBase, i, j: Int32;
+begin
+  // Note: this method allows w to be only (2 * x.Length - 1) words if result will fit
+  // if (w.Length != 2 * x.Length)
+  // throw new ArgumentException("no I don't think so...");
+
+  wBase := System.length(w) - 1;
+
+  i := System.length(x) - 1;
+
+  while i > 0 do
+
+  begin
+    v := UInt32(x[i]);
+
+    c := v * v + UInt32(w[wBase]);
+    w[wBase] := Int32(c);
+    c := c shr 32;
+
+    j := i - 1;
+    while j >= 0 do
+    begin
+      prod := v * UInt32(x[j]);
+
+      System.Dec(wBase);
+      c := c + ((UInt32(w[wBase]) and UIMASK) + (UInt32(prod) shl 1));
+      w[wBase] := Int32(c);
+      c := (c shr 32) + (prod shr 31);
+      System.Dec(j);
+    end;
+
+    System.Dec(wBase);
+    c := c + UInt32(w[wBase]);
+    w[wBase] := Int32(c);
+
+    System.Dec(wBase);
+    if (wBase >= 0) then
+    begin
+      w[wBase] := Int32(c shr 32);
+    end
+    else
+    begin
+{$IFDEF DEBUG}
+      System.Assert((c shr 32) = 0);
+{$ENDIF DEBUG}
+    end;
+
+    wBase := wBase + i;
+    System.Dec(i);
+  end;
+
+  c := UInt32(x[0]);
+
+  c := (c * c) + UInt32(w[wBase]);
+  w[wBase] := Int32(c);
+
+  System.Dec(wBase);
+  if (wBase >= 0) then
+  begin
+    w[wBase] := w[wBase] + Int32(c shr 32);
+  end
+  else
+  begin
+{$IFDEF DEBUG}
+    System.Assert((c shr 32) = 0);
+{$ENDIF DEBUG}
+  end;
+
+  Result := w;
+end;
+
+class procedure TBigInteger.SquareMonty(const a, x, m: TCryptoLibInt32Array;
+  mDash: UInt32; smallMontyModulus: Boolean);
+var
+  n, aMax, j, i: Int32;
+  xVal, a0: UInt32;
+  x0, carry, t, prod1, prod2, xi: UInt64;
+begin
+  // mDash = -m^(-1) mod b
+
+  n := System.length(m);
+
+  if (n = 1) then
+  begin
+    xVal := UInt32(x[0]);
+    x[0] := Int32(MultiplyMontyNIsOne(xVal, xVal, UInt32(m[0]), mDash));
+    Exit;
+  end;
+
+  x0 := UInt32(x[n - 1]);
+
+  carry := x0 * x0;
+
+  t := UInt32(UInt64(UInt32(carry)) * mDash);
+
+  prod2 := t * UInt32(m[n - 1]);
+  carry := carry + UInt32(prod2);
+
+{$IFDEF DEBUG}
+  System.Assert(UInt32(carry) = 0);
+{$ENDIF DEBUG}
+  carry := (carry shr 32) + (prod2 shr 32);
+
+  j := n - 2;
+  while j >= 0 do
+  begin
+    prod1 := x0 * UInt32(x[j]);
+    prod2 := t * UInt32(m[j]);
+
+    carry := carry + ((prod2 and UIMASK) + (UInt32(prod1) shl 1));
+    a[j + 2] := Int32(carry);
+    carry := (carry shr 32) + (prod1 shr 31) + (prod2 shr 32);
+    System.Dec(j);
+  end;
+
+  a[1] := Int32(carry);
+  aMax := Int32(carry shr 32);
+
+  i := n - 2;
+  while i >= 0 do
+  begin
+    a0 := UInt32(a[n]);
+    t := UInt32(UInt64(a0) * mDash);
+
+    carry := t * UInt32(m[n - 1]) + a0;
+
+{$IFDEF DEBUG}
+    System.Assert(UInt32(carry) = 0);
+{$ENDIF DEBUG}
+    carry := carry shr 32;
+
+    j := n - 2;
+    while j > i do
+    begin
+      carry := carry + (t * UInt32(m[j]) + UInt32(a[j + 1]));
+      a[j + 2] := Int32(carry);
+      carry := carry shr 32;
+      System.Dec(j);
+    end;
+
+    xi := UInt32(x[i]);
+
+    prod1 := xi * xi;
+    prod2 := t * UInt32(m[i]);
+
+    carry := carry + ((prod1 and UIMASK) + UInt32(prod2) + UInt32(a[i + 1]));
+    a[i + 2] := Int32(carry);
+    carry := (carry shr 32) + (prod1 shr 32) + (prod2 shr 32);
+
+    j := i - 1;
+    while j >= 0 do
+    begin
+      prod1 := xi * UInt32(x[j]);
+      prod2 := t * UInt32(m[j]);
+
+      carry := carry + ((prod2 and UIMASK) + (UInt32(prod1) shl 1) +
+        UInt32(a[j + 1]));
+      a[j + 2] := Int32(carry);
+      carry := (carry shr 32) + (prod1 shr 31) + (prod2 shr 32);
+      System.Dec(j);
+    end;
+
+    carry := carry + UInt32(aMax);
+    a[1] := Int32(carry);
+    aMax := Int32(carry shr 32);
+    System.Dec(i);
+  end;
+
+  a[0] := aMax;
+
+  if ((not smallMontyModulus) and (CompareTo(0, a, 0, m) >= 0)) then
+  begin
+    Subtract(0, a, 0, m);
+  end;
+
+  System.Move(a[1], x[0], n * System.SizeOf(Int32));
+
+end;
+
+function TBigInteger.Subtract(const n: TBigInteger): TBigInteger;
+var
+  compare: Int32;
+  bigun, lilun: TBigInteger;
+begin
+  if (n.Fsign = 0) then
+  begin
+    Result := Self;
+    Exit;
+  end;
+
+  if (Fsign = 0) then
+  begin
+    Result := n.Negate();
+    Exit;
+  end;
+
+  if (Fsign <> n.Fsign) then
+  begin
+    Result := Add(n.Negate());
+    Exit;
+  end;
+
+  compare := CompareNoLeadingZeroes(0, Fmagnitude, 0, n.Fmagnitude);
+  if (compare = 0) then
+  begin
+    Result := Zero;
+    Exit;
+  end;
+
+  if (compare < 0) then
+  begin
+    bigun := n;
+    lilun := Self;
+  end
+  else
+  begin
+    bigun := Self;
+    lilun := n;
+  end;
+
+  Result := TBigInteger.Create(Fsign * compare, doSubBigLil(bigun.Fmagnitude,
+    lilun.Fmagnitude), True);
+end;
+
+function TBigInteger.ToString(radix: Int32): String;
+var
+  firstNonZero, pos, mask, bits, i, scale: Int32;
+  sl: TStringList;
+  s: TList<String>;
+  moduli: TList<TBigInteger>;
+  u, q, r: TBigInteger;
+begin
+  // TODO Make this method work for other radices (ideally 2 <= radix <= 36 as in Java)
+  case (radix) of
+    2, 8, 10, 16:
+      begin
+        // do nothing because it is in valid supported range
+      end
+
+  else
+    begin
+      raise EFormatCryptoLibException.CreateRes(@SUnSupportedBase);
+    end;
+
+  end;
+
+  // NB: Can only happen to internally managed instances
+  if ((not FIsInitialized) and (Fmagnitude = Nil)) then
+  begin
+    Result := 'Nil';
+    Exit;
+  end;
+
+  if (Fsign = 0) then
+  begin
+    Result := '0';
+    Exit;
+  end;
+
+  // NOTE: This *should* be unnecessary, since the magnitude *should* never have leading zero digits
+  firstNonZero := 0;
+  while (firstNonZero < System.length(Fmagnitude)) do
+  begin
+    if (Fmagnitude[firstNonZero] <> 0) then
+    begin
+      break;
+    end;
+    System.Inc(firstNonZero);
+  end;
+
+  if (firstNonZero = System.length(Fmagnitude)) then
+  begin
+    Result := '0';
+    Exit;
+  end;
+
+  sl := TStringList.Create();
+  sl.LineBreak := '';
+  try
+
+    if (Fsign = -1) then
+    begin
+      sl.Add('-');
+    end;
+
+    case radix of
+      2:
+        begin
+          pos := firstNonZero;
+
+          sl.Add(TBigInteger.IntToBin(Fmagnitude[pos]));
+          System.Inc(pos);
+          while (pos < System.length(Fmagnitude)) do
+          begin
+            AppendZeroExtendedString(sl,
+              TBigInteger.IntToBin(Fmagnitude[pos]), 32);
+
+            System.Inc(pos);
+          end;
+
+        end;
+
+      8:
+        begin
+          mask := (1 shl 30) - 1;
+          u := Abs();
+          bits := u.BitLength;
+          s := TList<string>.Create();
+          try
+            while (bits > 30) do
+            begin
+              s.Add(TBigInteger.IntToOctal(u.Int32Value and mask));
+              u := u.ShiftRight(30);
+              bits := bits - 30;
+            end;
+            sl.Add(TBigInteger.IntToOctal(u.Int32Value));
+            i := s.Count - 1;
+            while i >= 0 do
+            begin
+              AppendZeroExtendedString(sl, s[i], 10);
+              System.Dec(i);
+            end;
+          finally
+            s.Free;
+          end;
+
+        end;
+
+      16:
+        begin
+          pos := firstNonZero;
+          sl.Add(IntToHex(Fmagnitude[pos], 2));
+          System.Inc(pos);
+          while (pos < System.length(Fmagnitude)) do
+          begin
+            AppendZeroExtendedString(sl, IntToHex(Fmagnitude[pos], 2), 8);
+            System.Inc(pos);
+          end;
+
+        end;
+      // TODO This could work for other radices if there is an alternative to Convert.ToString method
+      // default:
+      10:
+        begin
+          q := Abs();
+          if (q.BitLength < 64) then
+          begin
+            sl.Add(IntToStr(q.Int64Value));
+            Result := sl.Text;
+            Exit;
+          end;
+
+          // TODO Could cache the moduli for each radix (soft reference?)
+          moduli := TList<TBigInteger>.Create();
+          try
+            r := TBigInteger.ValueOf(radix);
+            while (r.CompareTo(q) <= 0) do
+            begin
+              moduli.Add(r);
+              r := r.Square();
+            end;
+
+            scale := moduli.Count;
+            sl.Capacity := sl.Capacity + (1 shl scale);
+
+            ToString(sl, radix, moduli, scale, q);
+          finally
+            moduli.Free;
+          end;
+
+        end;
+
+    end;
+
+    Result := LowerCase(sl.Text);
+
+  finally
+    sl.Free;
+  end;
+
+end;
+
+function TBigInteger.ToString: String;
+begin
+  Result := ToString(10);
+end;
+
+class function TBigInteger.GetWindowList(const mag: TCryptoLibInt32Array;
+  extraBits: Int32): TCryptoLibInt32Array;
+var
+  i, v, leadingBits, resultSize, resultPos, bitPos, mult, multLimit,
+    zeroes: Int32;
+begin
+
+  v := mag[0];
+{$IFDEF DEBUG}
+  System.Assert(v <> 0);
+{$ENDIF DEBUG}
+  leadingBits := BitLen(v);
+
+  resultSize := (((System.length(mag) - 1) shl 5) + leadingBits)
+    div (1 + extraBits) + 2;
+  System.SetLength(Result, resultSize);
+  resultPos := 0;
+
+  bitPos := 33 - leadingBits;
+  v := v shl bitPos;
+
+  mult := 1;
+  multLimit := 1 shl extraBits;
+  zeroes := 0;
+
+  i := 0;
+  while True do
+
+  begin
+    while bitPos < 32 do
+
+    begin
+      if (mult < multLimit) then
+      begin
+        mult := (mult shl 1) or Int32((UInt32(v) shr 31));
+      end
+      else if (v < 0) then
+      begin
+        Result[resultPos] := CreateWindowEntry(mult, zeroes);
+        System.Inc(resultPos);
+        mult := 1;
+        zeroes := 0;
+      end
+      else
+      begin
+        System.Inc(zeroes);
+      end;
+
+      v := v shl 1;
+      System.Inc(bitPos);
+    end;
+
+    System.Inc(i);
+    if (i = System.length(mag)) then
+    begin
+      Result[resultPos] := CreateWindowEntry(mult, zeroes);
+      System.Inc(resultPos);
+      break;
+    end;
+
+    v := mag[i];
+    bitPos := 0;
+  end;
+
+  Result[resultPos] := -1;
+
+end;
+
+function TBigInteger.CheckProbablePrime(certainty: Int32; const random: IRandom;
+  randomlySelected: Boolean): Boolean;
+var
+  numLists, i, j, prime, qRem, test: Int32;
+  primeList: TCryptoLibInt32Array;
+begin
+{$IFDEF DEBUG}
+  System.Assert(certainty > 0);
+  System.Assert(CompareTo(Two) > 0);
+  System.Assert(TestBit(0));
+{$ENDIF DEBUG}
+  // Try to reduce the penalty for really small numbers
+  numLists := Math.Min(BitLength - 1, System.length(primeLists));
+
+  for i := 0 to System.Pred(numLists) do
+  begin
+    test := Remainder(primeProducts[i]);
+
+    primeList := primeLists[i];
+
+    for j := 0 to System.Pred(System.length(primeList)) do
+
+    begin
+      prime := primeList[j];
+      qRem := test mod prime;
+      if (qRem = 0) then
+      begin
+        // We may find small numbers in the list
+        Result := (BitLength < 16) and (Int32Value = prime);
+        Exit;
+      end;
+    end;
+  end;
+
+  // TODO Special case for < 10^16 (RabinMiller fixed list)
+  // if (BitLength < 30)
+  // {
+  // RabinMiller against 2, 3, 5, 7, 11, 13, 23 is sufficient
+  // }
+
+  // TODO Is it worth trying to create a hybrid of these two?
+  Result := RabinMillerTest(certainty, random, randomlySelected);
+  // return SolovayStrassenTest(certainty, random);
+
+  // bool rbTest = RabinMillerTest(certainty, random);
+  // bool ssTest = SolovayStrassenTest(certainty, random);
+  //
+  // Debug.Assert(rbTest == ssTest);
+  //
+  // return rbTest;
+end;
+
+function TBigInteger.ClearBit(n: Int32): TBigInteger;
+begin
+  if (n < 0) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SInvalidBitAddress);
+  end;
+
+  if (not TestBit(n)) then
+  begin
+    Result := Self;
+    Exit;
+  end;
+
+  // TODO Handle negative values
+  if ((Fsign > 0) and (n < (BitLength - 1))) then
+  begin
+    Result := FlipExistingBit(n);
+    Exit;
+  end;
+
+  Result := AndNot(One.ShiftLeft(n));
+end;
+
+class function TBigInteger.CompareNoLeadingZeroes(xIndx: Int32;
+  const x: TCryptoLibInt32Array; yIndx: Int32;
+  const y: TCryptoLibInt32Array): Int32;
+var
+  diff: Int32;
+  v1, v2: UInt32;
+begin
+  diff := (System.length(x) - System.length(y)) - (xIndx - yIndx);
+
+  if (diff <> 0) then
+  begin
+    if diff < 0 then
+    begin
+      Result := -1;
+      Exit;
+    end
+    else
+    begin
+      Result := 1;
+      Exit;
+    end;
+
+  end;
+
+  // lengths of magnitudes the same, test the magnitude values
+
+  while (xIndx < System.length(x)) do
+  begin
+    v1 := UInt32(x[xIndx]);
+    System.Inc(xIndx);
+    v2 := UInt32(y[yIndx]);
+    System.Inc(yIndx);
+
+    if (v1 <> v2) then
+    begin
+
+      if v1 < v2 then
+      begin
+        Result := -1;
+        Exit;
+      end
+      else
+      begin
+        Result := 1;
+        Exit;
+      end;
+    end;
+  end;
+
+  Result := 0;
+end;
+
+class function TBigInteger.CompareTo(xIndx: Int32;
+  const x: TCryptoLibInt32Array; yIndx: Int32;
+  const y: TCryptoLibInt32Array): Int32;
+begin
+  while ((xIndx <> System.length(x)) and (x[xIndx] = 0)) do
+  begin
+    System.Inc(xIndx);
+  end;
+
+  while ((yIndx <> System.length(y)) and (y[yIndx] = 0)) do
+  begin
+    System.Inc(yIndx);
+  end;
+
+  Result := CompareNoLeadingZeroes(xIndx, x, yIndx, y);
+end;
+
+function TBigInteger.Divide(const x, y: TCryptoLibInt32Array)
+  : TCryptoLibInt32Array;
+var
+  xStart, yStart, xyCmp, yBitLength, xBitLength, shift, iCountStart, cBitLength,
+    cStart, len: Int32;
+  Count, iCount, c: TCryptoLibInt32Array;
+  firstC, firstX: UInt32;
+begin
+  xStart := 0;
+  while ((xStart < System.length(x)) and (x[xStart] = 0)) do
+  begin
+    System.Inc(xStart);
+  end;
+
+  yStart := 0;
+
+  while ((yStart < System.length(y)) and (y[yStart] = 0)) do
+  begin
+    System.Inc(yStart);
+  end;
+
+{$IFDEF DEBUG}
+  System.Assert(yStart < System.length(y));
+{$ENDIF DEBUG}
+  xyCmp := CompareNoLeadingZeroes(xStart, x, yStart, y);
+
+  if (xyCmp > 0) then
+  begin
+    yBitLength := CalcBitLength(1, yStart, y);
+    xBitLength := CalcBitLength(1, xStart, x);
+    shift := xBitLength - yBitLength;
+
+    iCountStart := 0;
+
+    cStart := 0;
+    cBitLength := yBitLength;
+    if (shift > 0) then
+    begin
+      // iCount = ShiftLeft(One.magnitude, shift);
+      System.SetLength(iCount, (shift shr 5) + 1);
+
+      iCount[0] := 1 shl (shift and 31);
+
+      c := ShiftLeft(y, shift);
+      cBitLength := cBitLength + shift;
+    end
+    else
+    begin
+      iCount := TCryptoLibInt32Array.Create(1);
+
+      len := System.length(y) - yStart;
+      System.SetLength(c, len);
+      System.Move(y[yStart], c[0], len * System.SizeOf(Int32));
+    end;
+
+    System.SetLength(Count, System.length(iCount));
+
+    while True do
+    begin
+      if ((cBitLength < xBitLength) or (CompareNoLeadingZeroes(xStart, x,
+        cStart, c) >= 0)) then
+      begin
+        Subtract(xStart, x, cStart, c);
+        AddMagnitudes(Count, iCount);
+
+        while (x[xStart] = 0) do
+        begin
+          System.Inc(xStart);
+          if (xStart = System.length(x)) then
+          begin
+            Result := Count;
+            Exit;
+          end;
+        end;
+
+        // xBitLength = CalcBitLength(xStart, x);
+        xBitLength := 32 * (System.length(x) - xStart - 1) + BitLen(x[xStart]);
+
+        if (xBitLength <= yBitLength) then
+        begin
+          if (xBitLength < yBitLength) then
+          begin
+            Result := Count;
+            Exit;
+          end;
+
+          xyCmp := CompareNoLeadingZeroes(xStart, x, yStart, y);
+
+          if (xyCmp <= 0) then
+          begin
+            break;
+          end;
+        end;
+      end;
+
+      shift := cBitLength - xBitLength;
+
+      // NB: The case where c[cStart] is 1-bit is harmless
+      if (shift = 1) then
+      begin
+        firstC := UInt32(c[cStart] shr 1);
+        firstX := UInt32(x[xStart]);
+        if (firstC > firstX) then
+        begin
+          System.Inc(shift);
+        end;
+      end;
+
+      if (shift < 2) then
+      begin
+        ShiftRightOneInPlace(cStart, c);
+        System.Dec(cBitLength);
+        ShiftRightOneInPlace(iCountStart, iCount);
+      end
+      else
+      begin
+        ShiftRightInPlace(cStart, c, shift);
+        cBitLength := cBitLength - shift;
+        ShiftRightInPlace(iCountStart, iCount, shift);
+      end;
+
+      // cStart = c.Length - ((cBitLength + 31) / 32);
+      while (c[cStart] = 0) do
+      begin
+        System.Inc(cStart);
+      end;
+
+      while (iCount[iCountStart] = 0) do
+      begin
+        System.Inc(iCountStart);
+      end;
+    end;
+  end
+  else
+  begin
+    System.SetLength(Count, 1);
+  end;
+
+  if (xyCmp = 0) then
+  begin
+    AddMagnitudes(Count, One.Fmagnitude);
+    TArrayUtils.Fill(x, xStart, System.length(x), Int32(0));
+  end;
+
+  Result := Count;
+end;
+
+function TBigInteger.Divide(const val: TBigInteger): TBigInteger;
+var
+  tempRes: TBigInteger;
+  mag: TCryptoLibInt32Array;
+begin
+  if (val.Fsign = 0) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SDivisionByZero);
+  end;
+
+  if (Fsign = 0) then
+  begin
+    Result := Zero;
+    Exit;
+  end;
+
+  if (val.QuickPow2Check()) then // val is power of two
+  begin
+    tempRes := Abs().ShiftRight(val.Abs().BitLength - 1);
+    if val.Fsign = Fsign then
+    begin
+      Result := tempRes;
+      Exit;
+    end
+    else
+    begin
+      Result := tempRes.Negate();
+      Exit;
+    end;
+
+  end;
+
+  mag := System.Copy(Fmagnitude);
+
+  Result := TBigInteger.Create(Fsign * val.Fsign,
+    Divide(mag, val.Fmagnitude), True);
+end;
+
+function TBigInteger.DivideAndRemainder(const val: TBigInteger)
+  : TCryptoLibGenericArray<TBigInteger>;
+var
+  biggies: TCryptoLibGenericArray<TBigInteger>;
+  e: Int32;
+  Quotient: TBigInteger;
+  Remainder, quotient_array: TCryptoLibInt32Array;
+begin
+  if (val.Fsign = 0) then
+  begin
+    raise EArithmeticCryptoLibException.CreateRes(@SDivisionByZero);
+  end;
+
+  System.SetLength(biggies, 2);
+
+  if (Fsign = 0) then
+  begin
+    biggies[0] := Zero;
+    biggies[1] := Zero;
+  end
+  else if (val.QuickPow2Check()) then // val is power of two
+  begin
+    e := val.Abs().BitLength - 1;
+    Quotient := Abs().ShiftRight(e);
+    Remainder := LastNBits(e);
+
+    if val.Fsign = Fsign then
+    begin
+      biggies[0] := Quotient
+    end
+    else
+    begin
+      biggies[0] := Quotient.Negate();
+    end;
+
+    biggies[1] := TBigInteger.Create(Fsign, Remainder, True);
+  end
+  else
+  begin
+    Remainder := System.Copy(Fmagnitude);
+    quotient_array := Divide(Remainder, val.Fmagnitude);
+
+    biggies[0] := TBigInteger.Create(Fsign * val.Fsign, quotient_array, True);
+    biggies[1] := TBigInteger.Create(Fsign, Remainder, True);
+  end;
+
+  Result := biggies;
+end;
+
+function TBigInteger.ToByteArray(unsigned: Boolean): TCryptoLibByteArray;
+var
+  nBits, nBytes, magIndex, bytesIndex: Int32;
+  mag, lastMag: UInt32;
+  bytes: TCryptoLibByteArray;
+  carry: Boolean;
+begin
+  if (Fsign = 0) then
+  begin
+    if unsigned then
+    begin
+      Result := FZeroEncoding;
+      Exit;
+    end
+    else
+    begin
+      System.SetLength(Result, 1);
+      Exit;
+    end;
+
+  end;
+
+  if ((unsigned) and (Fsign > 0)) then
+  begin
+    nBits := BitLength;
+  end
+  else
+  begin
+    nBits := BitLength + 1;
+  end;
+
+  nBytes := GetByteLength(nBits);
+  System.SetLength(bytes, nBytes);
+
+  magIndex := System.length(Fmagnitude);
+  bytesIndex := System.length(bytes);
+
+  if (Fsign > 0) then
+  begin
+    while (magIndex > 1) do
+    begin
+      System.Dec(magIndex);
+      mag := UInt32(Fmagnitude[magIndex]);
+      System.Dec(bytesIndex);
+      bytes[bytesIndex] := Byte(mag);
+      System.Dec(bytesIndex);
+      bytes[bytesIndex] := Byte(mag shr 8);
+      System.Dec(bytesIndex);
+      bytes[bytesIndex] := Byte(mag shr 16);
+      System.Dec(bytesIndex);
+      bytes[bytesIndex] := Byte(mag shr 24);
+    end;
+
+    lastMag := UInt32(Fmagnitude[0]);
+    while (lastMag > System.High(Byte)) do
+    begin
+      System.Dec(bytesIndex);
+      bytes[bytesIndex] := Byte(lastMag);
+      lastMag := lastMag shr 8;
+    end;
+
+    System.Dec(bytesIndex);
+    bytes[bytesIndex] := Byte(lastMag);
+  end
+  else // sign < 0
+  begin
+    carry := True;
+
+    while (magIndex > 1) do
+    begin
+      System.Dec(magIndex);
+      mag := not(UInt32(Fmagnitude[magIndex]));
+
+      if (carry) then
+      begin
+        System.Inc(mag);
+        carry := (mag = System.Low(UInt32));
+      end;
+
+      System.Dec(bytesIndex);
+      bytes[bytesIndex] := Byte(mag);
+      System.Dec(bytesIndex);
+      bytes[bytesIndex] := Byte(mag shr 8);
+      System.Dec(bytesIndex);
+      bytes[bytesIndex] := Byte(mag shr 16);
+      System.Dec(bytesIndex);
+      bytes[bytesIndex] := Byte(mag shr 24);
+
+    end;
+
+    lastMag := UInt32(Fmagnitude[0]);
+
+    if (carry) then
+    begin
+      // Never wraps because magnitude[0] != 0
+      System.Dec(lastMag);
+    end;
+
+    while (lastMag > System.High(Byte)) do
+    begin
+      System.Dec(bytesIndex);
+      bytes[bytesIndex] := Byte(not lastMag);
+      lastMag := lastMag shr 8;
+    end;
+
+    System.Dec(bytesIndex);
+    bytes[bytesIndex] := Byte(not lastMag);
+
+    if (bytesIndex > 0) then
+    begin
+      System.Dec(bytesIndex);
+      bytes[bytesIndex] := System.High(Byte);
+    end;
+  end;
+
+  Result := bytes;
+end;
+
+function TBigInteger.ToByteArray: TCryptoLibByteArray;
+begin
+  Result := ToByteArray(false);
+end;
+
+function TBigInteger.ToByteArrayUnsigned: TCryptoLibByteArray;
+begin
+  Result := ToByteArray(True);
+end;
+
+end.

+ 225 - 0
src/libraries/cryptolib4pascal/ClpBigIntegers.pas

@@ -0,0 +1,225 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpBigIntegers;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Math,
+  ClpBigInteger,
+  ClpCryptoLibTypes,
+  ClpISecureRandom;
+
+resourcestring
+  SInvalidLength = 'Standard Length Exceeded, "n"';
+  SInvalidMinValue = '"min" may not be greater than "max""';
+
+type
+
+  /// <summary>
+  /// BigInteger utilities.
+  /// </summary>
+  TBigIntegers = class abstract(TObject)
+
+  strict private
+  const
+    MaxIterations = Int32(1000);
+
+  public
+
+    /// <summary>
+    /// Return the passed in value as an unsigned byte array.
+    /// </summary>
+    /// <param name="n">
+    /// value to be converted.
+    /// </param>
+    /// <returns>
+    /// a byte array without a leading zero byte if present in the signed
+    /// encoding.
+    /// </returns>
+    class function AsUnsignedByteArray(const n: TBigInteger)
+      : TCryptoLibByteArray; overload; static;
+
+    /// <summary>
+    /// the passed in value as an unsigned byte array of specified length,
+    /// zero-extended as necessary.
+    /// </summary>
+    /// <param name="length">
+    /// length desired length of result array.
+    /// </param>
+    /// <param name="n">
+    /// value to be converted.
+    /// </param>
+    /// <returns>
+    /// a byte array of specified length, with leading zeroes as necessary
+    /// given the size of n.
+    /// </returns>
+    class function AsUnsignedByteArray(length: Int32; const n: TBigInteger)
+      : TCryptoLibByteArray; overload; static;
+
+    /// <summary>
+    /// Return a random BigInteger not less than 'min' and not greater than
+    /// 'max'
+    /// </summary>
+    /// <param name="min">
+    /// the least value that may be generated
+    /// </param>
+    /// <param name="max">
+    /// the greatest value that may be generated
+    /// </param>
+    /// <param name="random">
+    /// the source of randomness
+    /// </param>
+    /// <returns>
+    /// a random BigInteger value in the range [min,max]
+    /// </returns>
+    class function CreateRandomInRange(const min, max: TBigInteger;
+      // TODO Should have been just Random class
+      const random: ISecureRandom): TBigInteger; static;
+
+    /// <summary>
+    /// <para>
+    /// The regular <b>BigInteger.toByteArray()</b> includes the sign bit
+    /// of the number and <br />might result in an extra byte addition.
+    /// This method removes this extra byte.
+    /// </para>
+    /// </summary>
+    /// <param name="b">
+    /// the integer to format into a byte array
+    /// </param>
+    /// <param name="numBytes">
+    /// the desired size of the resulting byte array
+    /// </param>
+    /// <returns>
+    /// numBytes byte long array.
+    /// </returns>
+    class function BigIntegerToBytes(const b: TBigInteger; numBytes: Int32)
+      : TCryptoLibByteArray; static; inline;
+
+    class function GetUnsignedByteLength(const n: TBigInteger): Int32;
+      static; inline;
+
+  end;
+
+implementation
+
+{ TBigIntegers }
+
+class function TBigIntegers.AsUnsignedByteArray(const n: TBigInteger)
+  : TCryptoLibByteArray;
+begin
+  Result := n.ToByteArrayUnsigned();
+end;
+
+class function TBigIntegers.AsUnsignedByteArray(length: Int32;
+  const n: TBigInteger): TCryptoLibByteArray;
+var
+  bytes: TCryptoLibByteArray;
+begin
+  bytes := n.ToByteArrayUnsigned();
+
+  if (System.length(bytes) > length) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidLength);
+  end;
+
+  if (System.length(bytes) = length) then
+  begin
+    Result := bytes;
+    Exit;
+  end;
+
+  System.SetLength(Result, length);
+  System.Move(bytes[0], Result[System.length(Result) - System.length(bytes)],
+    System.length(bytes) * System.SizeOf(Byte));
+
+end;
+
+class function TBigIntegers.BigIntegerToBytes(const b: TBigInteger;
+  numBytes: Int32): TCryptoLibByteArray;
+var
+  biBytes: TCryptoLibByteArray;
+  start, length: Int32;
+begin
+  System.SetLength(Result, numBytes);
+  biBytes := b.ToByteArray();
+  if System.length(biBytes) = (numBytes + 1) then
+  begin
+    start := 1
+  end
+  else
+  begin
+    start := 0
+  end;
+
+  length := min(System.length(biBytes), numBytes);
+  System.Move(biBytes[start], Result[numBytes - length],
+    length * System.SizeOf(Byte));
+end;
+
+class function TBigIntegers.CreateRandomInRange(const min, max: TBigInteger;
+  // TODO Should have been just Random class
+  const random: ISecureRandom): TBigInteger;
+var
+  cmp, I: Int32;
+  x: TBigInteger;
+begin
+  cmp := min.CompareTo(max);
+  if (cmp >= 0) then
+  begin
+    if (cmp > 0) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SInvalidMinValue);
+    end;
+
+    Result := min;
+    Exit;
+  end;
+
+  if (min.BitLength > (max.BitLength shr 1)) then
+  begin
+    Result := CreateRandomInRange(TBigInteger.Zero, max.Subtract(min),
+      random).Add(min);
+    Exit;
+  end;
+
+  I := 0;
+  while I < MaxIterations do
+  begin
+    x := TBigInteger.Create(max.BitLength, random);
+    if ((x.CompareTo(min) >= 0) and (x.CompareTo(max) <= 0)) then
+    begin
+      Result := x;
+      Exit;
+    end;
+    System.Inc(I);
+  end;
+
+  // fall back to a faster (restricted) method
+  Result := TBigInteger.Create(max.Subtract(min).BitLength - 1, random)
+    .Add(min);
+end;
+
+class function TBigIntegers.GetUnsignedByteLength(const n: TBigInteger): Int32;
+begin
+  Result := (n.BitLength + 7) shr 3;
+end;
+
+end.

+ 525 - 0
src/libraries/cryptolib4pascal/ClpBitConverter.pas

@@ -0,0 +1,525 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpBitConverter;
+
+{$I CryptoLib.inc}
+{ /*  Some code here were translated from Microsoft .NET Framework source on Github */ }
+
+interface
+
+uses
+  ClpCryptoLibTypes;
+
+type
+
+  TBitConverter = class sealed(TObject)
+
+  strict private
+
+    class var
+
+      FIsLittleEndian: Boolean;
+
+    class function GetHexValue(i: Int32): Char; static; inline;
+    class function GetIsLittleEndian(): Boolean; static; inline;
+    class constructor BitConverter();
+
+  public
+
+    class property IsLittleEndian: Boolean read GetIsLittleEndian;
+
+    { ==================================================================== }
+
+    class function GetBytes(value: Boolean): TCryptoLibByteArray; overload;
+      static; inline;
+    class function GetBytes(value: Char): TCryptoLibByteArray; overload;
+      static; inline;
+    class function GetBytes(value: Double): TCryptoLibByteArray; overload;
+      static; inline;
+    class function GetBytes(value: Int16): TCryptoLibByteArray; overload;
+      static; inline;
+    class function GetBytes(value: Int32): TCryptoLibByteArray; overload;
+      static; inline;
+    class function GetBytes(value: Int64): TCryptoLibByteArray; overload;
+      static; inline;
+    class function GetBytes(value: Single): TCryptoLibByteArray; overload;
+      static; inline;
+    class function GetBytes(value: UInt8): TCryptoLibByteArray; overload;
+      static; inline;
+    class function GetBytes(value: UInt16): TCryptoLibByteArray; overload;
+      static; inline;
+    class function GetBytes(value: UInt32): TCryptoLibByteArray; overload;
+      static; inline;
+    class function GetBytes(value: UInt64): TCryptoLibByteArray; overload;
+      static; inline;
+
+    { ==================================================================== }
+
+    class function ToBoolean(const value: TCryptoLibByteArray;
+      StartIndex: Int32): Boolean; static; inline;
+    class function ToChar(const value: TCryptoLibByteArray; StartIndex: Int32)
+      : Char; static; inline;
+    class function ToDouble(const value: TCryptoLibByteArray; StartIndex: Int32)
+      : Double; static; inline;
+    class function ToInt16(const value: TCryptoLibByteArray; StartIndex: Int32)
+      : Int16; static; inline;
+    class function ToInt32(const value: TCryptoLibByteArray; StartIndex: Int32)
+      : Int32; static; inline;
+    class function ToInt64(const value: TCryptoLibByteArray; StartIndex: Int32)
+      : Int64; static; inline;
+    class function ToSingle(const value: TCryptoLibByteArray; StartIndex: Int32)
+      : Single; static; inline;
+    class function ToString(const value: TCryptoLibByteArray): String;
+      reintroduce; overload; static;
+    class function ToString(const value: TCryptoLibByteArray; StartIndex: Int32)
+      : String; reintroduce; overload; static;
+    class function ToString(const value: TCryptoLibByteArray;
+      StartIndex, &Length: Int32): String; reintroduce; overload; static;
+    class function ToUInt8(const value: TCryptoLibByteArray; StartIndex: Int32)
+      : UInt8; static; inline;
+    class function ToUInt16(const value: TCryptoLibByteArray; StartIndex: Int32)
+      : UInt16; static; inline;
+    class function ToUInt32(const value: TCryptoLibByteArray; StartIndex: Int32)
+      : UInt32; overload; static; inline;
+
+    class function ToUInt64(const value: TCryptoLibByteArray; StartIndex: Int32)
+      : UInt64; overload; static; inline;
+
+  end;
+
+implementation
+
+{ TBitConverter }
+
+class constructor TBitConverter.BitConverter;
+var
+  IntValue: Int32;
+  PIIntValueAddress: PInteger;
+  PBIntValueAddress: PByte;
+  ByteValue: Byte;
+begin
+  IntValue := 1;
+  PIIntValueAddress := @IntValue;
+  PBIntValueAddress := PByte(PIIntValueAddress);
+  ByteValue := PBIntValueAddress^;
+  FIsLittleEndian := ByteValue = 1;
+end;
+
+{ ==================================================================== }
+
+class function TBitConverter.GetBytes(value: Int16): TCryptoLibByteArray;
+begin
+
+  // System.SetLength(result, System.SizeOf(value));
+  // PSmallInt(@result[0])^ := value;
+  System.SetLength(result, System.SizeOf(value));
+  System.Move(value, result[0], System.SizeOf(value));
+end;
+
+class function TBitConverter.GetBytes(value: Int32): TCryptoLibByteArray;
+begin
+
+  // System.SetLength(result, System.SizeOf(value));
+  // PInteger(@result[0])^ := value;
+  System.SetLength(result, System.SizeOf(value));
+  System.Move(value, result[0], System.SizeOf(value));
+
+end;
+
+class function TBitConverter.GetBytes(value: Double): TCryptoLibByteArray;
+begin
+
+  // System.SetLength(result, System.SizeOf(value));
+  // PDouble(@result[0])^ := value;
+  System.SetLength(result, System.SizeOf(value));
+  System.Move(value, result[0], System.SizeOf(value));
+end;
+
+class function TBitConverter.GetBytes(value: Boolean): TCryptoLibByteArray;
+begin
+
+  // System.SetLength(result, System.SizeOf(value));
+  // PBoolean(@result[0])^ := value;
+  System.SetLength(result, System.SizeOf(value));
+  System.Move(value, result[0], System.SizeOf(value));
+end;
+
+class function TBitConverter.GetBytes(value: Char): TCryptoLibByteArray;
+begin
+
+  // System.SetLength(result, System.SizeOf(value));
+  // PChar(@result[0])^ := value;
+  System.SetLength(result, System.SizeOf(value));
+  System.Move(value, result[0], System.SizeOf(value));
+end;
+
+class function TBitConverter.GetBytes(value: UInt8): TCryptoLibByteArray;
+begin
+  // System.SetLength(result, System.SizeOf(value));
+  // PByte(@result[0])^ := value;
+  System.SetLength(result, System.SizeOf(value));
+  System.Move(value, result[0], System.SizeOf(value));
+end;
+
+class function TBitConverter.GetBytes(value: UInt16): TCryptoLibByteArray;
+begin
+
+  // System.SetLength(result, System.SizeOf(value));
+  // PWord(@result[0])^ := value;
+  System.SetLength(result, System.SizeOf(value));
+  System.Move(value, result[0], System.SizeOf(value));
+end;
+
+class function TBitConverter.GetBytes(value: Int64): TCryptoLibByteArray;
+begin
+
+  // System.SetLength(result, System.SizeOf(value));
+  // PInt64(@result[0])^ := value;
+  System.SetLength(result, System.SizeOf(value));
+  System.Move(value, result[0], System.SizeOf(value));
+end;
+
+class function TBitConverter.GetBytes(value: Single): TCryptoLibByteArray;
+begin
+
+  // System.SetLength(result, System.SizeOf(value));
+  // PSingle(@result[0])^ := value;
+  System.SetLength(result, System.SizeOf(value));
+  System.Move(value, result[0], System.SizeOf(value));
+end;
+
+class function TBitConverter.GetBytes(value: UInt32): TCryptoLibByteArray;
+begin
+
+  // System.SetLength(result, System.SizeOf(value));
+  // PCardinal(@result[0])^ := value;
+  System.SetLength(result, System.SizeOf(value));
+  System.Move(value, result[0], System.SizeOf(value));
+end;
+
+class function TBitConverter.GetBytes(value: UInt64): TCryptoLibByteArray;
+begin
+
+  // System.SetLength(result, System.SizeOf(value));
+  // PUInt64(@result[0])^ := value;
+  System.SetLength(result, System.SizeOf(value));
+  System.Move(value, result[0], System.SizeOf(value));
+end;
+
+{ ==================================================================== }
+
+class function TBitConverter.GetHexValue(i: Int32): Char;
+begin
+  if i < 10 then
+    result := Char(i + System.Ord('0'))
+  else
+    result := Char((i - 10) + System.Ord('A'));
+end;
+
+class function TBitConverter.GetIsLittleEndian: Boolean;
+begin
+  result := FIsLittleEndian;
+end;
+
+{ ==================================================================== }
+
+class function TBitConverter.ToBoolean(const value: TCryptoLibByteArray;
+  StartIndex: Int32): Boolean;
+begin
+  // result := PBoolean(@value[StartIndex])^;
+  System.Move(value[StartIndex], result, System.SizeOf(result));
+
+end;
+
+class function TBitConverter.ToChar(const value: TCryptoLibByteArray;
+  StartIndex: Int32): Char;
+begin
+  // System.Move(value[StartIndex], result, System.SizeOf(result));
+
+  if (IsLittleEndian) then
+  begin
+    result := Char(value[StartIndex] or (value[StartIndex + 1] shl 8));
+    Exit;
+  end
+  else
+  begin
+
+    result := Char((value[StartIndex] shl 8) or value[StartIndex + 1]);
+    Exit;
+  end;
+
+end;
+
+class function TBitConverter.ToDouble(const value: TCryptoLibByteArray;
+  StartIndex: Int32): Double;
+var
+  i1, i2: Int32;
+  val: Int64;
+begin
+  // System.Move(value[StartIndex], result, System.SizeOf(result));
+
+  if (IsLittleEndian) then
+  begin
+    i1 := value[StartIndex] or (value[StartIndex + 1] shl 8) or
+      (value[StartIndex + 2] shl 16) or (value[StartIndex + 3] shl 24);
+    i2 := (value[StartIndex + 4]) or (value[StartIndex + 5] shl 8) or
+      (value[StartIndex + 6] shl 16) or (value[StartIndex + 7] shl 24);
+    val := UInt32(i1) or (Int64(i2) shl 32);
+    result := PDouble(@val)^;
+    Exit;
+  end
+  else
+  begin
+
+    i1 := (value[StartIndex] shl 24) or (value[StartIndex + 1] shl 16) or
+      (value[StartIndex + 2] shl 8) or (value[StartIndex + 3]);
+    i2 := (value[StartIndex + 4] shl 24) or (value[StartIndex + 5] shl 16) or
+      (value[StartIndex + 6] shl 8) or (value[StartIndex + 7]);
+    val := UInt32(i2) or (Int64(i1) shl 32);
+    result := PDouble(@val)^;
+    Exit;
+  end;
+
+end;
+
+class function TBitConverter.ToInt16(const value: TCryptoLibByteArray;
+  StartIndex: Int32): Int16;
+begin
+
+  // System.Move(value[StartIndex], result, System.SizeOf(result));
+
+  if (IsLittleEndian) then
+  begin
+    result := SmallInt(value[StartIndex] or (value[StartIndex + 1] shl 8));
+    Exit;
+  end
+  else
+  begin
+
+    result := SmallInt((value[StartIndex] shl 8) or value[StartIndex + 1]);
+    Exit;
+  end;
+
+end;
+
+class function TBitConverter.ToInt32(const value: TCryptoLibByteArray;
+  StartIndex: Int32): Int32;
+begin
+  // System.Move(value[StartIndex], result, System.SizeOf(result));
+
+  if (IsLittleEndian) then
+  begin
+    result := value[StartIndex] or (value[StartIndex + 1] shl 8) or
+      (value[StartIndex + 2] shl 16) or (value[StartIndex + 3] shl 24);
+    Exit;
+  end
+  else
+  begin
+
+    result := (value[StartIndex] shl 24) or (value[StartIndex + 1] shl 16) or
+      (value[StartIndex + 2] shl 8) or (value[StartIndex + 3]);
+    Exit;
+  end;
+
+end;
+
+class function TBitConverter.ToInt64(const value: TCryptoLibByteArray;
+  StartIndex: Int32): Int64;
+var
+  i1, i2: Int32;
+begin
+  // System.Move(value[StartIndex], result, System.SizeOf(result));
+  if (IsLittleEndian) then
+  begin
+    i1 := value[StartIndex] or (value[StartIndex + 1] shl 8) or
+      (value[StartIndex + 2] shl 16) or (value[StartIndex + 3] shl 24);
+    i2 := (value[StartIndex + 4]) or (value[StartIndex + 5] shl 8) or
+      (value[StartIndex + 6] shl 16) or (value[StartIndex + 7] shl 24);
+    result := UInt32(i1) or (Int64(i2) shl 32);
+    Exit;
+  end
+  else
+  begin
+
+    i1 := (value[StartIndex] shl 24) or (value[StartIndex + 1] shl 16) or
+      (value[StartIndex + 2] shl 8) or (value[StartIndex + 3]);
+    i2 := (value[StartIndex + 4] shl 24) or (value[StartIndex + 5] shl 16) or
+      (value[StartIndex + 6] shl 8) or (value[StartIndex + 7]);
+    result := UInt32(i2) or (Int64(i1) shl 32);
+    Exit;
+  end;
+end;
+
+class function TBitConverter.ToSingle(const value: TCryptoLibByteArray;
+  StartIndex: Int32): Single;
+var
+  val: Int32;
+begin
+  // System.Move(value[StartIndex], result, System.SizeOf(result));
+  if (IsLittleEndian) then
+  begin
+    val := (value[StartIndex] or (value[StartIndex + 1] shl 8) or
+      (value[StartIndex + 2] shl 16) or (value[StartIndex + 3] shl 24));
+    result := PSingle(@val)^;
+    Exit;
+  end
+  else
+  begin
+    val := (value[StartIndex] shl 24) or (value[StartIndex + 1] shl 16) or
+      (value[StartIndex + 2] shl 8) or (value[StartIndex + 3]);
+    result := PSingle(@val)^;
+    Exit;
+  end;
+
+end;
+
+class function TBitConverter.ToString(const value: TCryptoLibByteArray): String;
+var
+  LowVal: Int32;
+begin
+
+{$IFDEF DELPHIXE2_UP}
+  LowVal := System.Low(value);
+{$ELSE}
+  LowVal := 0;
+{$ENDIF DELPHIXE2_UP}
+  result := ToString(value, LowVal);
+end;
+
+class function TBitConverter.ToString(const value: TCryptoLibByteArray;
+  StartIndex: Int32): String;
+begin
+  result := ToString(value, StartIndex, System.Length(value) - StartIndex);
+end;
+
+class function TBitConverter.ToString(const value: TCryptoLibByteArray;
+  StartIndex, &Length: Int32): String;
+
+var
+  Idx, Index, chArrayLength, LowVal: Int32;
+  chArray: TCryptoLibCharArray;
+  b: Byte;
+
+begin
+  result := '';
+
+  chArrayLength := &Length * 3;
+
+  System.SetLength(chArray, chArrayLength);
+  Idx := 0;
+  Index := StartIndex;
+  while Idx < chArrayLength do
+  begin
+    b := value[Index];
+    System.Inc(Index);
+
+    // chArray[Idx] := GetHexValue(b div 16);
+    // chArray[Idx + 1] := GetHexValue(b mod 16);
+    chArray[Idx] := GetHexValue(b shr 4);
+    chArray[Idx + 1] := GetHexValue(b and 15);
+    chArray[Idx + 2] := '-';
+
+    System.Inc(Idx, 3);
+  end;
+
+{$IFDEF DELPHIXE2_UP}
+  LowVal := System.Low(chArray);
+{$ELSE}
+  LowVal := 0;
+{$ENDIF DELPHIXE2_UP}
+  System.SetString(result, PChar(@chArray[LowVal]), System.Length(chArray) - 1);
+
+end;
+
+class function TBitConverter.ToUInt8(const value: TCryptoLibByteArray;
+  StartIndex: Int32): UInt8;
+begin
+  // result := PByte(@value[StartIndex])^;
+  System.Move(value[StartIndex], result, System.SizeOf(result));
+end;
+
+class function TBitConverter.ToUInt16(const value: TCryptoLibByteArray;
+  StartIndex: Int32): UInt16;
+begin
+  // System.Move(value[StartIndex], result, System.SizeOf(result));
+
+  if (IsLittleEndian) then
+  begin
+    result := Word(value[StartIndex] or (value[StartIndex + 1] shl 8));
+    Exit;
+  end
+  else
+  begin
+
+    result := Word((value[StartIndex] shl 8) or value[StartIndex + 1]);
+    Exit;
+  end;
+end;
+
+class function TBitConverter.ToUInt32(const value: TCryptoLibByteArray;
+  StartIndex: Int32): UInt32;
+begin
+  // System.Move(value[StartIndex], result, System.SizeOf(result));
+
+  if (IsLittleEndian) then
+  begin
+    result := UInt32(value[StartIndex] or (value[StartIndex + 1] shl 8) or
+      (value[StartIndex + 2] shl 16) or (value[StartIndex + 3] shl 24));
+    Exit;
+  end
+  else
+  begin
+
+    result := UInt32((value[StartIndex] shl 24) or
+      (value[StartIndex + 1] shl 16) or (value[StartIndex + 2] shl 8) or
+      (value[StartIndex + 3]));
+    Exit;
+  end;
+end;
+
+class function TBitConverter.ToUInt64(const value: TCryptoLibByteArray;
+  StartIndex: Int32): UInt64;
+var
+  i1, i2: Int32;
+begin
+
+  // System.Move(value[StartIndex], result, System.SizeOf(result));
+
+  if (IsLittleEndian) then
+  begin
+    i1 := value[StartIndex] or (value[StartIndex + 1] shl 8) or
+      (value[StartIndex + 2] shl 16) or (value[StartIndex + 3] shl 24);
+    i2 := (value[StartIndex + 4]) or (value[StartIndex + 5] shl 8) or
+      (value[StartIndex + 6] shl 16) or (value[StartIndex + 7] shl 24);
+    result := UInt64(UInt32(i1) or (Int64(i2) shl 32));
+    Exit;
+  end
+  else
+  begin
+
+    i1 := (value[StartIndex] shl 24) or (value[StartIndex + 1] shl 16) or
+      (value[StartIndex + 2] shl 8) or (value[StartIndex + 3]);
+    i2 := (value[StartIndex + 4] shl 24) or (value[StartIndex + 5] shl 16) or
+      (value[StartIndex + 6] shl 8) or (value[StartIndex + 7]);
+    result := UInt64(UInt32(i2) or (Int64(i1) shl 32));
+    Exit;
+  end;
+
+end;
+
+end.

+ 373 - 0
src/libraries/cryptolib4pascal/ClpBits.pas

@@ -0,0 +1,373 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpBits;
+
+{$I CryptoLib.inc}
+
+interface
+
+type
+  TBits = class sealed(TObject)
+
+  public
+
+    class function ReverseBytesInt32(Value: Int32): Int32; static; inline;
+    class function ReverseBitsUInt8(Value: UInt8): UInt8; static; inline;
+    class function ReverseBytesUInt16(Value: UInt16): UInt16; static; inline;
+    class function ReverseBytesUInt32(Value: UInt32): UInt32; static; inline;
+    class function ReverseBytesUInt64(Value: UInt64): UInt64; static; inline;
+
+    /// <summary>
+    /// Reverse a ByteArray.
+    /// </summary>
+    /// Implementation was found here <see cref="http://stackoverflow.com/a/12969282" />
+    /// <param name="Source">Pointer to Input Array.</param>
+    /// <param name="Dest">Pointer to Destination Array.</param>
+    /// <param name="Size">Size of the Array to Reverse.</param>
+
+    class procedure ReverseByteArray(Source, Dest: Pointer;
+      Size: Int64); static;
+
+    /// <summary>
+    /// Calculates Arithmetic shift right.
+    /// </summary>
+    /// <param name="AValue">Int32 value to compute 'Asr' on.</param>
+    /// <param name="AShiftBits">Byte, number of bits to shift value to.</param>
+    /// <returns>Shifted value.</returns>
+    /// <remarks>
+    /// Emulated Implementation was gotten from FreePascal sources
+    /// </remarks>
+
+    class function Asr32(AValue: Int32; AShiftBits: Byte): Int32;
+      static; inline;
+
+    /// <summary>
+    /// Calculates Arithmetic shift right.
+    /// </summary>
+    /// <param name="AValue">Int64 value to compute 'Asr' on.</param>
+    /// <param name="AShiftBits">Byte, number of bits to shift value to.</param>
+    /// <returns>Shifted value.</returns>
+    /// <remarks>
+    /// Emulated Implementation was gotten from FreePascal sources
+    /// </remarks>
+
+    class function Asr64(AValue: Int64; AShiftBits: Byte): Int64;
+      static; inline;
+
+    /// <summary>
+    /// Calculates Negative Left Shift. This was implemented to circumvent a
+    /// bug in FPC ARM when performing Shift Left on certain values with a
+    /// Negative Shift Bits. For example UInt32(1948415963) shl Int32(-2)
+    /// should give "3221225472" but in FPC ARM, It gives "0". In some C
+    /// Compilers, this is "Undefined"
+    /// </summary>
+    /// <param name="Value">
+    /// Value to Perform Shift On
+    /// </param>
+    /// <param name="ShiftBits">
+    /// Integer, number of bits to shift value to. This Number <b>Must be
+    /// Negative</b>
+    /// </param>
+    /// <param name="value">
+    /// UInt32 value to compute 'NLS' on.
+    /// </param>
+    /// <returns>
+    /// Shifted value.
+    /// </returns>
+
+    class function NegativeLeftShift32(Value: UInt32; ShiftBits: Int32): UInt32;
+      static; inline;
+
+    /// <summary>
+    /// Calculates Negative Right Shift. This was implemented to circumvent a
+    /// compiler issue when performing Shift Right on certain values with a
+    /// Negative Shift Bits. In some C Compilers, this is "Undefined"
+    /// </summary>
+    /// <param name="Value">
+    /// Value to Perform Shift On
+    /// </param>
+    /// <param name="ShiftBits">
+    /// Integer, number of bits to shift value to. This Number <b>Must be
+    /// Negative</b>
+    /// </param>
+    /// <param name="value">
+    /// UInt32 value to compute 'NRS' on.
+    /// </param>
+    /// <returns>
+    /// Shifted value.
+    /// </returns>
+
+    class function NegativeRightShift32(Value: UInt32; ShiftBits: Int32)
+      : UInt32; static; inline;
+
+    /// <summary>
+    /// Calculates Negative Right Shift. This was implemented to circumvent a
+    /// compiler issue when performing Shift Right on certain values with a
+    /// Negative Shift Bits. In some C Compilers, this is "Undefined"
+    /// </summary>
+    /// <param name="Value">
+    /// Value to Perform Shift On
+    /// </param>
+    /// <param name="ShiftBits">
+    /// Integer, number of bits to shift value to. This Number <b>Must be
+    /// Negative</b>
+    /// </param>
+    /// <param name="value">
+    /// UInt64 value to compute 'NRS' on.
+    /// </param>
+    /// <returns>
+    /// Shifted value.
+    /// </returns>
+
+    class function NegativeRightShift64(Value: UInt64; ShiftBits: Int32)
+      : UInt64; static; inline;
+
+    class function RotateLeft8(a_value: Byte; a_n: Int32): Byte; static; inline;
+    class function RotateLeft32(a_value: UInt32; a_n: Int32): UInt32;
+      static; inline;
+    class function RotateLeft64(a_value: UInt64; a_n: Int32): UInt64;
+      static; inline;
+    class function RotateRight8(a_value: Byte; a_n: Int32): Byte;
+      static; inline;
+    class function RotateRight32(a_value: UInt32; a_n: Int32): UInt32;
+      static; inline;
+    class function RotateRight64(a_value: UInt64; a_n: Int32): UInt64;
+      static; inline;
+
+  end;
+
+implementation
+
+{ TBits }
+
+class procedure TBits.ReverseByteArray(Source, Dest: Pointer; Size: Int64);
+var
+  ptr_src, ptr_dest: PByte;
+begin
+  ptr_src := PByte(Source);
+  ptr_dest := PByte(Dest);
+  System.Inc(ptr_dest, Size - 1);
+  while Size > 0 do
+  begin
+    ptr_dest^ := ptr_src^;
+    System.Inc(ptr_src);
+    System.Dec(ptr_dest);
+    System.Dec(Size);
+  end;
+end;
+
+class function TBits.ReverseBytesInt32(Value: Int32): Int32;
+{$IFNDEF FPC}
+var
+  i1, i2, i3, i4: Int32;
+{$ENDIF FPC}
+begin
+{$IFDEF FPC}
+  Result := SwapEndian(Value);
+{$ELSE}
+  i1 := Value and $FF;
+  i2 := TBits.Asr32(Value, 8) and $FF;
+  i3 := TBits.Asr32(Value, 16) and $FF;
+  i4 := TBits.Asr32(Value, 24) and $FF;
+
+  Result := (i1 shl 24) or (i2 shl 16) or (i3 shl 8) or (i4 shl 0);
+{$ENDIF FPC}
+end;
+
+class function TBits.ReverseBitsUInt8(Value: UInt8): UInt8;
+begin
+  Value := ((Value shr 1) and $55) or ((Value shl 1) and $AA);
+  Value := ((Value shr 2) and $33) or ((Value shl 2) and $CC);
+  Value := ((Value shr 4) and $0F) or ((Value shl 4) and $F0);
+  Result := Value;
+end;
+
+class function TBits.ReverseBytesUInt16(Value: UInt16): UInt16;
+begin
+{$IFDEF FPC}
+  Result := SwapEndian(Value);
+{$ELSE}
+  Result := UInt16((Value and UInt32($FF)) shl 8 or
+    (Value and UInt32($FF00)) shr 8);
+{$ENDIF FPC}
+end;
+
+class function TBits.ReverseBytesUInt32(Value: UInt32): UInt32;
+begin
+{$IFDEF FPC}
+  Result := SwapEndian(Value);
+{$ELSE}
+  Result := (Value and UInt32($000000FF)) shl 24 or (Value and UInt32($0000FF00)
+    ) shl 8 or (Value and UInt32($00FF0000)) shr 8 or
+    (Value and UInt32($FF000000)) shr 24;
+{$ENDIF FPC}
+end;
+
+class function TBits.ReverseBytesUInt64(Value: UInt64): UInt64;
+begin
+{$IFDEF FPC}
+  Result := SwapEndian(Value);
+{$ELSE}
+  Result := (Value and UInt64($00000000000000FF)) shl 56 or
+    (Value and UInt64($000000000000FF00)) shl 40 or
+    (Value and UInt64($0000000000FF0000)) shl 24 or
+    (Value and UInt64($00000000FF000000)) shl 8 or
+    (Value and UInt64($000000FF00000000)) shr 8 or
+    (Value and UInt64($0000FF0000000000)) shr 24 or
+    (Value and UInt64($00FF000000000000)) shr 40 or
+    (Value and UInt64($FF00000000000000)) shr 56;
+{$ENDIF FPC}
+end;
+
+class function TBits.Asr32(AValue: Int32; AShiftBits: Byte): Int32;
+
+begin
+{$IFDEF FPC}
+  Result := SarLongInt(AValue, AShiftBits);
+{$ELSE}
+  Result := Int32(UInt32(UInt32(UInt32(AValue) shr (AShiftBits and 31)) or
+    (UInt32(Int32(UInt32(0 - UInt32(UInt32(AValue) shr 31)) and
+    UInt32(Int32(0 - (Ord((AShiftBits and 31) <> 0) { and 1 } )))))
+    shl (32 - (AShiftBits and 31)))));
+{$ENDIF FPC}
+end;
+
+class function TBits.Asr64(AValue: Int64; AShiftBits: Byte): Int64;
+begin
+{$IFDEF FPC}
+  Result := SarInt64(AValue, AShiftBits);
+{$ELSE}
+  Result := Int64(UInt64(UInt64(UInt64(AValue) shr (AShiftBits and 63)) or
+    (UInt64(Int64(UInt64(0 - UInt64(UInt64(AValue) shr 63)) and
+    UInt64(Int64(0 - (Ord((AShiftBits and 63) <> 0) { and 1 } )))))
+    shl (64 - (AShiftBits and 63)))));
+{$ENDIF FPC}
+end;
+
+class function TBits.RotateLeft8(a_value: Byte; a_n: Int32): Byte;
+begin
+{$IFDEF DEBUG}
+  System.Assert(a_n >= 0);
+{$ENDIF DEBUG}
+{$IFDEF FPC}
+  Result := RolByte(a_value, a_n);
+{$ELSE}
+  a_n := a_n and 7;
+
+  Result := (a_value shl a_n) or (a_value shr (8 - a_n));
+{$ENDIF FPC}
+end;
+
+class function TBits.NegativeLeftShift32(Value: UInt32;
+  ShiftBits: Int32): UInt32;
+begin
+{$IFDEF DEBUG}
+  System.Assert(ShiftBits < 0);
+{$ENDIF DEBUG}
+  Result := Value shl (32 + ShiftBits);
+end;
+
+class function TBits.NegativeRightShift32(Value: UInt32;
+  ShiftBits: Int32): UInt32;
+begin
+{$IFDEF DEBUG}
+  System.Assert(ShiftBits < 0);
+{$ENDIF DEBUG}
+  Result := Value shr (32 + ShiftBits);
+end;
+
+class function TBits.NegativeRightShift64(Value: UInt64;
+  ShiftBits: Int32): UInt64;
+begin
+{$IFDEF DEBUG}
+  System.Assert(ShiftBits < 0);
+{$ENDIF DEBUG}
+  Result := Value shr (64 + ShiftBits);
+end;
+
+class function TBits.RotateLeft32(a_value: UInt32; a_n: Int32): UInt32;
+begin
+{$IFDEF DEBUG}
+  System.Assert(a_n >= 0);
+{$ENDIF DEBUG}
+{$IFDEF FPC}
+  Result := RolDWord(a_value, a_n);
+{$ELSE}
+  a_n := a_n and 31;
+
+  Result := (a_value shl a_n) or (a_value shr (32 - a_n));
+{$ENDIF FPC}
+end;
+
+class function TBits.RotateLeft64(a_value: UInt64; a_n: Int32): UInt64;
+begin
+{$IFDEF DEBUG}
+  System.Assert(a_n >= 0);
+{$ENDIF DEBUG}
+{$IFDEF FPC}
+  Result := RolQWord(a_value, a_n);
+{$ELSE}
+  a_n := a_n and 63;
+
+  Result := (a_value shl a_n) or (a_value shr (64 - a_n));
+{$ENDIF FPC}
+end;
+
+class function TBits.RotateRight8(a_value: Byte; a_n: Int32): Byte;
+begin
+{$IFDEF DEBUG}
+  System.Assert(a_n >= 0);
+{$ENDIF DEBUG}
+{$IFDEF FPC}
+  Result := RorByte(a_value, a_n);
+{$ELSE}
+  a_n := a_n and 7;
+
+  Result := (a_value shr a_n) or (a_value shl (8 - a_n));
+{$ENDIF FPC}
+end;
+
+class function TBits.RotateRight32(a_value: UInt32; a_n: Int32): UInt32;
+begin
+{$IFDEF DEBUG}
+  System.Assert(a_n >= 0);
+{$ENDIF DEBUG}
+{$IFDEF FPC}
+  Result := RorDWord(a_value, a_n);
+{$ELSE}
+  a_n := a_n and 31;
+
+  Result := (a_value shr a_n) or (a_value shl (32 - a_n));
+{$ENDIF FPC}
+end;
+
+class function TBits.RotateRight64(a_value: UInt64; a_n: Int32): UInt64;
+begin
+{$IFDEF DEBUG}
+  System.Assert(a_n >= 0);
+{$ENDIF DEBUG}
+{$IFDEF FPC}
+  Result := RorQWord(a_value, a_n);
+{$ELSE}
+  a_n := a_n and 63;
+
+  Result := (a_value shr a_n) or (a_value shl (64 - a_n));
+{$ENDIF FPC}
+end;
+
+end.

+ 1200 - 0
src/libraries/cryptolib4pascal/ClpBlockCipherModes.pas

@@ -0,0 +1,1200 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpBlockCipherModes;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Math,
+  SysUtils,
+  ClpIBlockCipher,
+  ClpICipherParameters,
+  ClpIParametersWithIV,
+  ClpArrayUtils,
+  ClpCryptoLibTypes,
+  ClpIBlockCipherModes;
+
+resourcestring
+  SInvalidIVLength =
+    'Initialisation Vector Must be the Same Length as Block Size';
+  SInvalidChangeState = 'Cannot Change Encrypting State Without Providing Key.';
+  SInputBufferTooShort = 'Input Buffer too Short';
+  SOutputBufferTooShort = 'Output Buffer too Short';
+{$IFNDEF _FIXINSIGHT_}
+  SInvalidParameterArgument = 'CTR/SIC Mode Requires ParametersWithIV';
+  SInvalidTooLargeIVLength =
+    'CTR/SIC mode requires IV no greater than: %u bytes';
+  SInvalidTooSmallIVLength = 'CTR/SIC mode requires IV of at least: %u bytes';
+{$ENDIF}
+
+type
+
+  /// <summary>
+  /// implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
+  /// </summary>
+  TCbcBlockCipher = class sealed(TInterfacedObject, ICbcBlockCipher,
+    IBlockCipher)
+
+  strict private
+
+  var
+    FIV, FcbcV, FcbcNextV: TCryptoLibByteArray;
+    FblockSize: Int32;
+    Fcipher: IBlockCipher;
+    Fencrypting: Boolean;
+
+    /// <summary>
+    /// return the algorithm name and mode.
+    /// </summary>
+    /// <returns>
+    /// return the name of the underlying algorithm followed by "/CBC"
+    /// </returns>
+    function GetAlgorithmName: String; inline;
+
+    function GetIsPartialBlockOkay: Boolean; inline;
+
+    /// <summary>
+    /// Do the appropriate chaining step for CBC mode encryption.
+    /// </summary>
+    /// <param name="input">
+    /// the array containing the data to be encrypted.
+    /// </param>
+    /// <param name="inOff">
+    /// offset into the in array the data starts at.
+    /// </param>
+    /// <param name="outBytes">
+    /// the array the encrypted data will be copied into.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset into the out array the output will start at.
+    /// </param>
+    /// <returns>
+    /// the number of bytes processed and produced.
+    /// </returns>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if there isn't enough data in input, or space in output.
+    /// </exception>
+    /// <exception cref="EInvalidOperationCryptoLibException">
+    /// if the cipher isn't initialised.
+    /// </exception>
+    function EncryptBlock(const input: TCryptoLibByteArray; inOff: Int32;
+      const outBytes: TCryptoLibByteArray; outOff: Int32): Int32;
+
+    /// <summary>
+    /// Do the appropriate chaining step for CBC mode decryption.
+    /// </summary>
+    /// <param name="input">
+    /// the array containing the data to be decrypted.
+    /// </param>
+    /// <param name="inOff">
+    /// offset into the in array the data starts at.
+    /// </param>
+    /// <param name="outBytes">
+    /// the array the decrypted data will be copied into.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset into the out array the output will start at.
+    /// </param>
+    /// <returns>
+    /// the number of bytes processed and produced.
+    /// </returns>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if there isn't enough data in input, or space in output.
+    /// </exception>
+    /// <exception cref="EInvalidOperationCryptoLibException">
+    /// if the cipher isn't initialised.
+    /// </exception>
+    function DecryptBlock(const input: TCryptoLibByteArray; inOff: Int32;
+      const outBytes: TCryptoLibByteArray; outOff: Int32): Int32;
+
+  public
+
+    /// <summary>
+    /// Basic constructor.
+    /// </summary>
+    /// <param name="cipher">
+    /// the block cipher to be used as the basis of chaining.
+    /// </param>
+    constructor Create(const cipher: IBlockCipher);
+
+    /// <summary>
+    /// return the underlying block cipher that we are wrapping.
+    /// </summary>
+    /// <returns>
+    /// return the underlying block cipher that we are wrapping.
+    /// </returns>
+    function GetUnderlyingCipher(): IBlockCipher;
+
+    /// <summary>
+    /// Initialise the cipher and, possibly, the initialisation vector (IV). <br />
+    /// If an IV isn't passed as part of the parameter, the IV will be all
+    /// zeros.
+    /// </summary>
+    /// <param name="forEncryption">
+    /// forEncryption if true the cipher is initialised for encryption, if
+    /// false for decryption.
+    /// </param>
+    /// <param name="parameters">
+    /// the key and other data required by the cipher.
+    /// </param>
+    /// <exception cref="EArgumentCryptoLibException">
+    /// if the parameters argument is inappropriate
+    /// </exception>
+    procedure Init(forEncryption: Boolean; const parameters: ICipherParameters);
+
+    /// <summary>
+    /// return the block size of the underlying cipher.
+    /// </summary>
+    /// <returns>
+    /// return the block size of the underlying cipher.
+    /// </returns>
+    function GetBlockSize(): Int32; inline;
+
+    /// <summary>
+    /// Process one block of input from the input array and write it to the
+    /// output array.
+    /// </summary>
+    /// <param name="input">
+    /// the array containing the input data.
+    /// </param>
+    /// <param name="inOff">
+    /// offset into the input array the data starts at.
+    /// </param>
+    /// <param name="output">
+    /// the array the output data will be copied into.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset into the output array the data starts at.
+    /// </param>
+    /// <returns>
+    /// the number of bytes processed and produced.
+    /// </returns>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if there isn't enough data in input, or space in output.
+    /// </exception>
+    /// <exception cref="EInvalidOperationCryptoLibException">
+    /// if the cipher isn't initialised.
+    /// </exception>
+    function ProcessBlock(const input: TCryptoLibByteArray; inOff: Int32;
+      const output: TCryptoLibByteArray; outOff: Int32): Int32;
+
+    /// <summary>
+    /// reset the chaining vector back to the IV and reset the underlying
+    /// cipher.
+    /// </summary>
+    procedure Reset(); inline;
+
+    /// <summary>
+    /// return the algorithm name and mode.
+    /// </summary>
+    /// <value>
+    /// return the name of the underlying algorithm followed by "/CBC"
+    /// </value>
+    property AlgorithmName: String read GetAlgorithmName;
+
+    property IsPartialBlockOkay: Boolean read GetIsPartialBlockOkay;
+
+  end;
+
+type
+
+  /// <summary>
+  /// implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
+  /// </summary>
+  TCfbBlockCipher = class sealed(TInterfacedObject, ICfbBlockCipher,
+    IBlockCipher)
+
+  strict private
+
+  var
+    FIV, FcfbV, FcfbOutV: TCryptoLibByteArray;
+    FblockSize: Int32;
+    Fcipher: IBlockCipher;
+    Fencrypting: Boolean;
+
+    /// <summary>
+    /// return the algorithm name and mode.
+    /// </summary>
+    /// <returns>
+    /// return the name of the underlying algorithm followed by "/CFB"
+    /// </returns>
+    function GetAlgorithmName: String; inline;
+
+    function GetIsPartialBlockOkay: Boolean; inline;
+
+    /// <summary>
+    /// Do the appropriate processing for CFB mode encryption.
+    /// </summary>
+    /// <param name="input">
+    /// the array containing the data to be encrypted.
+    /// </param>
+    /// <param name="inOff">
+    /// offset into the in array the data starts at.
+    /// </param>
+    /// <param name="outBytes">
+    /// the array the encrypted data will be copied into.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset into the out array the output will start at.
+    /// </param>
+    /// <returns>
+    /// the number of bytes processed and produced.
+    /// </returns>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if there isn't enough data in input, or space in output.
+    /// </exception>
+    /// <exception cref="EInvalidOperationCryptoLibException">
+    /// if the cipher isn't initialised.
+    /// </exception>
+    function EncryptBlock(const input: TCryptoLibByteArray; inOff: Int32;
+      const outBytes: TCryptoLibByteArray; outOff: Int32): Int32;
+
+    /// <summary>
+    /// Do the appropriate chaining step for CBC mode decryption.
+    /// </summary>
+    /// <param name="input">
+    /// the array containing the data to be decrypted.
+    /// </param>
+    /// <param name="inOff">
+    /// offset into the in array the data starts at.
+    /// </param>
+    /// <param name="outBytes">
+    /// the array the decrypted data will be copied into.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset into the out array the output will start at.
+    /// </param>
+    /// <returns>
+    /// the number of bytes processed and produced.
+    /// </returns>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if there isn't enough data in input, or space in output.
+    /// </exception>
+    /// <exception cref="EInvalidOperationCryptoLibException">
+    /// if the cipher isn't initialised.
+    /// </exception>
+    function DecryptBlock(const input: TCryptoLibByteArray; inOff: Int32;
+      const outBytes: TCryptoLibByteArray; outOff: Int32): Int32;
+
+  public
+
+    /// <summary>
+    /// Basic constructor.
+    /// </summary>
+    /// <param name="cipher">
+    /// the block cipher to be used as the basis of the feedback mode.
+    /// </param>
+    /// <param name="bitBlockSize">
+    /// the block size in bits (note: a multiple of 8)
+    /// </param>
+    constructor Create(const cipher: IBlockCipher; bitBlockSize: Int32);
+
+    /// <summary>
+    /// return the underlying block cipher that we are wrapping.
+    /// </summary>
+    /// <returns>
+    /// return the underlying block cipher that we are wrapping.
+    /// </returns>
+    function GetUnderlyingCipher(): IBlockCipher;
+
+    /// <summary>
+    /// Initialise the cipher and, possibly, the initialisation vector (IV). <br />
+    /// If an IV isn't passed as part of the parameter, the IV will be all
+    /// zeros.
+    /// An IV which is too short is handled in FIPS compliant fashion.
+    /// </summary>
+    /// <param name="forEncryption">
+    /// forEncryption if true the cipher is initialised for encryption, if
+    /// false for decryption.
+    /// </param>
+    /// <param name="parameters">
+    /// the key and other data required by the cipher.
+    /// </param>
+    procedure Init(forEncryption: Boolean; const parameters: ICipherParameters);
+
+    /// <summary>
+    /// return the block size we are operating at.
+    /// </summary>
+    /// <returns>
+    /// the block size we are operating at (in bytes).
+    /// </returns>
+    function GetBlockSize(): Int32; inline;
+
+    /// <summary>
+    /// Process one block of input from the input array and write it to the
+    /// output array.
+    /// </summary>
+    /// <param name="input">
+    /// the array containing the input data.
+    /// </param>
+    /// <param name="inOff">
+    /// offset into the input array the data starts at.
+    /// </param>
+    /// <param name="output">
+    /// the array the output data will be copied into.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset into the output array the data starts at.
+    /// </param>
+    /// <returns>
+    /// the number of bytes processed and produced.
+    /// </returns>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if there isn't enough data in input, or space in output.
+    /// </exception>
+    /// <exception cref="EInvalidOperationCryptoLibException">
+    /// if the cipher isn't initialised.
+    /// </exception>
+    function ProcessBlock(const input: TCryptoLibByteArray; inOff: Int32;
+      const output: TCryptoLibByteArray; outOff: Int32): Int32;
+
+    /// <summary>
+    /// reset the chaining vector back to the IV and reset the underlying
+    /// cipher.
+    /// </summary>
+    procedure Reset(); inline;
+
+    /// <summary>
+    /// return the algorithm name and mode.
+    /// </summary>
+    /// <value>
+    /// return the name of the underlying algorithm followed by "/CFB"
+    /// </value>
+    property AlgorithmName: String read GetAlgorithmName;
+
+    property IsPartialBlockOkay: Boolean read GetIsPartialBlockOkay;
+
+  end;
+
+type
+
+  /// <summary>
+  /// implements a Output-FeedBack (OFB) mode on top of a simple cipher.
+  /// </summary>
+  TOfbBlockCipher = class sealed(TInterfacedObject, IOfbBlockCipher,
+    IBlockCipher)
+
+  strict private
+
+  var
+    FIV, FofbV, FofbOutV: TCryptoLibByteArray;
+    FblockSize: Int32;
+    Fcipher: IBlockCipher;
+    Fencrypting: Boolean;
+
+    /// <summary>
+    /// return the algorithm name and mode.
+    /// </summary>
+    /// <returns>
+    /// return the name of the underlying algorithm followed by "/OFB"
+    /// </returns>
+    function GetAlgorithmName: String; inline;
+
+    function GetIsPartialBlockOkay: Boolean; inline;
+
+  public
+
+    /// <summary>
+    /// Basic constructor.
+    /// </summary>
+    /// <param name="cipher">
+    /// the block cipher to be used as the basis of the feedback mode.
+    /// </param>
+    /// <param name="blockSize">
+    /// the block size in bits (note: a multiple of 8)
+    /// </param>
+    constructor Create(const cipher: IBlockCipher; blockSize: Int32);
+
+    /// <summary>
+    /// return the underlying block cipher that we are wrapping.
+    /// </summary>
+    /// <returns>
+    /// return the underlying block cipher that we are wrapping.
+    /// </returns>
+    function GetUnderlyingCipher(): IBlockCipher;
+
+    /// <summary>
+    /// Initialise the cipher and, possibly, the initialisation vector (IV). <br />
+    /// If an IV isn't passed as part of the parameter, the IV will be all
+    /// zeros.
+    /// An IV which is too short is handled in FIPS compliant fashion.
+    /// </summary>
+    /// <param name="forEncryption">
+    /// forEncryption if true the cipher is initialised for encryption, if
+    /// false for decryption.
+    /// ignored by this OFB mode though
+    /// </param>
+    /// <param name="parameters">
+    /// the key and other data required by the cipher.
+    /// </param>
+    procedure Init(forEncryption: Boolean; const parameters: ICipherParameters);
+
+    /// <summary>
+    /// return the block size we are operating at.
+    /// </summary>
+    /// <returns>
+    /// the block size we are operating at (in bytes).
+    /// </returns>
+    function GetBlockSize(): Int32; inline;
+
+    /// <summary>
+    /// Process one block of input from the input array and write it to the
+    /// output array.
+    /// </summary>
+    /// <param name="input">
+    /// the array containing the input data.
+    /// </param>
+    /// <param name="inOff">
+    /// offset into the input array the data starts at.
+    /// </param>
+    /// <param name="output">
+    /// the array the output data will be copied into.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset into the output array the data starts at.
+    /// </param>
+    /// <returns>
+    /// the number of bytes processed and produced.
+    /// </returns>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if there isn't enough data in input, or space in output.
+    /// </exception>
+    /// <exception cref="EInvalidOperationCryptoLibException">
+    /// if the cipher isn't initialised.
+    /// </exception>
+    function ProcessBlock(const input: TCryptoLibByteArray; inOff: Int32;
+      const output: TCryptoLibByteArray; outOff: Int32): Int32;
+
+    /// <summary>
+    /// reset the chaining vector back to the IV and reset the underlying
+    /// cipher.
+    /// </summary>
+    procedure Reset(); inline;
+
+    /// <summary>
+    /// return the algorithm name and mode.
+    /// </summary>
+    /// <value>
+    /// return the name of the underlying algorithm followed by "/OFB"
+    /// </value>
+    property AlgorithmName: String read GetAlgorithmName;
+
+    property IsPartialBlockOkay: Boolean read GetIsPartialBlockOkay;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Implements the Segmented Integer Counter (SIC) mode on top of a simple block cipher.
+  /// </summary>
+  TSicBlockCipher = class sealed(TInterfacedObject, ISicBlockCipher,
+    IBlockCipher)
+
+  strict private
+
+  var
+    FIV, Fcounter, FcounterOut: TCryptoLibByteArray;
+    FblockSize: Int32;
+    Fcipher: IBlockCipher;
+
+    /// <summary>
+    /// return the algorithm name and mode.
+    /// </summary>
+    /// <returns>
+    /// return the name of the underlying algorithm followed by "/SIC"
+    /// </returns>
+    function GetAlgorithmName: String; inline;
+
+    function GetIsPartialBlockOkay: Boolean; inline;
+
+  public
+
+    /// <summary>
+    /// Basic constructor.
+    /// </summary>
+    /// <param name="cipher">
+    /// the block cipher to be used.
+    /// </param>
+    constructor Create(const cipher: IBlockCipher);
+
+    /// <summary>
+    /// return the underlying block cipher that we are wrapping.
+    /// </summary>
+    /// <returns>
+    /// return the underlying block cipher that we are wrapping.
+    /// </returns>
+    function GetUnderlyingCipher(): IBlockCipher;
+
+    /// <summary>
+    /// Initialise the cipher and, possibly, the initialisation vector (IV). <br />
+    /// If an IV isn't passed as part of the parameter, the IV will be all
+    /// zeros.
+    /// An IV which is required in this mode.
+    /// </summary>
+    /// <param name="forEncryption">
+    /// forEncryption if true the cipher is initialised for encryption, if
+    /// false for decryption.
+    /// ignored by this CTR mode though
+    /// </param>
+    /// <param name="parameters">
+    /// the key and other data required by the cipher.
+    /// </param>
+    procedure Init(forEncryption: Boolean; const parameters: ICipherParameters);
+
+    /// <summary>
+    /// return the block size we are operating at.
+    /// </summary>
+    /// <returns>
+    /// the block size we are operating at (in bytes).
+    /// </returns>
+    function GetBlockSize(): Int32; inline;
+
+    /// <summary>
+    /// Process one block of input from the input array and write it to the
+    /// output array.
+    /// </summary>
+    /// <param name="input">
+    /// the array containing the input data.
+    /// </param>
+    /// <param name="inOff">
+    /// offset into the input array the data starts at.
+    /// </param>
+    /// <param name="output">
+    /// the array the output data will be copied into.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset into the output array the data starts at.
+    /// </param>
+    /// <returns>
+    /// the number of bytes processed and produced.
+    /// </returns>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if there isn't enough data in input, or space in output.
+    /// </exception>
+    /// <exception cref="EInvalidOperationCryptoLibException">
+    /// if the cipher isn't initialised.
+    /// </exception>
+    function ProcessBlock(const input: TCryptoLibByteArray; inOff: Int32;
+      const output: TCryptoLibByteArray; outOff: Int32): Int32;
+
+    /// <summary>
+    /// reset the chaining vector back to the IV and reset the underlying
+    /// cipher.
+    /// </summary>
+    procedure Reset(); inline;
+
+    /// <summary>
+    /// return the algorithm name and mode.
+    /// </summary>
+    /// <value>
+    /// return the name of the underlying algorithm followed by "/SIC"
+    /// </value>
+    property AlgorithmName: String read GetAlgorithmName;
+
+    property IsPartialBlockOkay: Boolean read GetIsPartialBlockOkay;
+
+  end;
+
+implementation
+
+{ TCbcBlockCipher }
+
+constructor TCbcBlockCipher.Create(const cipher: IBlockCipher);
+begin
+  inherited Create();
+  Fcipher := cipher;
+  FblockSize := cipher.GetBlockSize();
+
+  System.SetLength(FIV, FblockSize);
+  System.SetLength(FcbcV, FblockSize);
+  System.SetLength(FcbcNextV, FblockSize);
+end;
+
+function TCbcBlockCipher.DecryptBlock(const input: TCryptoLibByteArray;
+  inOff: Int32; const outBytes: TCryptoLibByteArray; outOff: Int32): Int32;
+var
+  length, I: Int32;
+  tmp: TCryptoLibByteArray;
+begin
+  if ((inOff + FblockSize) > System.length(input)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
+  end;
+
+  System.Move(input[inOff], FcbcNextV[0], FblockSize * System.SizeOf(Byte));
+
+  length := Fcipher.ProcessBlock(input, inOff, outBytes, outOff);
+
+
+  // XOR the FcbcV and the output
+
+  for I := 0 to System.Pred(FblockSize) do
+  begin
+    outBytes[outOff + I] := outBytes[outOff + I] xor FcbcV[I];
+  end;
+
+
+  // swap the back up buffer into next position
+
+  tmp := FcbcV;
+  FcbcV := FcbcNextV;
+  FcbcNextV := tmp;
+
+  result := &length;
+end;
+
+function TCbcBlockCipher.EncryptBlock(const input: TCryptoLibByteArray;
+  inOff: Int32; const outBytes: TCryptoLibByteArray; outOff: Int32): Int32;
+var
+  I, &length: Int32;
+begin
+  if ((inOff + FblockSize) > System.length(input)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
+  end;
+
+  // XOR the FcbcV and the input, then encrypt the FcbcV
+
+  for I := 0 to System.Pred(FblockSize) do
+  begin
+    FcbcV[I] := FcbcV[I] xor input[inOff + I];
+  end;
+
+  &length := Fcipher.ProcessBlock(FcbcV, 0, outBytes, outOff);
+
+
+  // copy ciphertext to FcbcV
+
+  System.Move(outBytes[outOff], FcbcV[0], System.length(FcbcV) *
+    System.SizeOf(Byte));
+
+  result := &length;
+end;
+
+procedure TCbcBlockCipher.Reset;
+begin
+  System.Move(FIV[0], FcbcV[0], System.length(FIV));
+  TArrayUtils.Fill(FcbcNextV, 0, System.length(FcbcNextV), Byte(0));
+
+  Fcipher.Reset();
+end;
+
+function TCbcBlockCipher.GetAlgorithmName: String;
+begin
+  result := Fcipher.AlgorithmName + '/CBC';
+end;
+
+function TCbcBlockCipher.GetBlockSize: Int32;
+begin
+  result := Fcipher.GetBlockSize();
+end;
+
+function TCbcBlockCipher.GetIsPartialBlockOkay: Boolean;
+begin
+  result := false;
+end;
+
+function TCbcBlockCipher.GetUnderlyingCipher: IBlockCipher;
+begin
+  result := Fcipher;
+end;
+
+procedure TCbcBlockCipher.Init(forEncryption: Boolean;
+  const parameters: ICipherParameters);
+var
+  oldEncrypting: Boolean;
+  ivParam: IParametersWithIV;
+  iv: TCryptoLibByteArray;
+  Lparameters: ICipherParameters;
+begin
+  oldEncrypting := Fencrypting;
+  Fencrypting := forEncryption;
+  Lparameters := parameters;
+
+  if Supports(Lparameters, IParametersWithIV, ivParam) then
+  begin
+    iv := ivParam.GetIV();
+
+    if (System.length(iv) <> FblockSize) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SInvalidIVLength);
+    end;
+
+    System.Move(iv[0], FIV[0], System.length(iv) * System.SizeOf(Byte));
+
+    Lparameters := ivParam.parameters;
+  end;
+
+  Reset();
+
+  // if Nil it's an IV changed only.
+  if (Lparameters <> Nil) then
+  begin
+    Fcipher.Init(Fencrypting, Lparameters);
+  end
+  else if (oldEncrypting <> Fencrypting) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidChangeState);
+  end;
+
+end;
+
+function TCbcBlockCipher.ProcessBlock(const input: TCryptoLibByteArray;
+  inOff: Int32; const output: TCryptoLibByteArray; outOff: Int32): Int32;
+begin
+  if Fencrypting then
+  begin
+    result := EncryptBlock(input, inOff, output, outOff);
+  end
+  else
+  begin
+    result := DecryptBlock(input, inOff, output, outOff);
+  end;
+end;
+
+{ TCfbBlockCipher }
+
+constructor TCfbBlockCipher.Create(const cipher: IBlockCipher;
+  bitBlockSize: Int32);
+begin
+  inherited Create();
+  Fcipher := cipher;
+  FblockSize := bitBlockSize div 8;
+
+  System.SetLength(FIV, Fcipher.GetBlockSize);
+  System.SetLength(FcfbV, Fcipher.GetBlockSize);
+  System.SetLength(FcfbOutV, Fcipher.GetBlockSize);
+end;
+
+function TCfbBlockCipher.DecryptBlock(const input: TCryptoLibByteArray;
+  inOff: Int32; const outBytes: TCryptoLibByteArray; outOff: Int32): Int32;
+var
+  I, count: Int32;
+begin
+  if ((inOff + FblockSize) > System.length(input)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
+  end;
+
+  if ((outOff + FblockSize) > System.length(outBytes)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
+  end;
+
+  Fcipher.ProcessBlock(FcfbV, 0, FcfbOutV, 0);
+
+  //
+  // change over the input block.
+  //
+  count := (System.length(FcfbV) - FblockSize) * System.SizeOf(Byte);
+  if count > 0 then
+  begin
+    System.Move(FcfbV[FblockSize], FcfbV[0], count);
+  end;
+
+  System.Move(input[inOff], FcfbV[(System.length(FcfbV) - FblockSize)],
+    FblockSize * System.SizeOf(Byte));
+
+  // XOR the FcfbV with the ciphertext producing the plaintext
+
+  for I := 0 to System.Pred(FblockSize) do
+  begin
+    outBytes[outOff + I] := Byte(FcfbOutV[I] xor input[inOff + I]);
+  end;
+
+  result := FblockSize;
+end;
+
+function TCfbBlockCipher.EncryptBlock(const input: TCryptoLibByteArray;
+  inOff: Int32; const outBytes: TCryptoLibByteArray; outOff: Int32): Int32;
+var
+  I, count: Int32;
+begin
+  if ((inOff + FblockSize) > System.length(input)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
+  end;
+
+  if ((outOff + FblockSize) > System.length(outBytes)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
+  end;
+
+  Fcipher.ProcessBlock(FcfbV, 0, FcfbOutV, 0);
+
+  // XOR the FcfbV with the plaintext producing the ciphertext
+
+  for I := 0 to System.Pred(FblockSize) do
+  begin
+    outBytes[outOff + I] := Byte(FcfbOutV[I] xor input[inOff + I]);
+  end;
+
+  //
+  // change over the input block.
+  //
+  count := (System.length(FcfbV) - FblockSize) * System.SizeOf(Byte);
+
+  if count > 0 then
+  begin
+    System.Move(FcfbV[FblockSize], FcfbV[0], count);
+  end;
+
+  System.Move(outBytes[outOff], FcfbV[(System.length(FcfbV) - FblockSize)],
+    FblockSize * System.SizeOf(Byte));
+
+  result := FblockSize;
+end;
+
+procedure TCfbBlockCipher.Reset;
+begin
+  System.Move(FIV[0], FcfbV[0], System.length(FIV));
+
+  Fcipher.Reset();
+end;
+
+function TCfbBlockCipher.GetAlgorithmName: String;
+begin
+  result := Fcipher.AlgorithmName + '/CFB' + IntToStr(FblockSize * 8);
+end;
+
+function TCfbBlockCipher.GetBlockSize: Int32;
+begin
+  result := FblockSize;
+end;
+
+function TCfbBlockCipher.GetIsPartialBlockOkay: Boolean;
+begin
+  result := true;
+end;
+
+function TCfbBlockCipher.GetUnderlyingCipher: IBlockCipher;
+begin
+  result := Fcipher;
+end;
+
+procedure TCfbBlockCipher.Init(forEncryption: Boolean;
+  const parameters: ICipherParameters);
+var
+  ivParam: IParametersWithIV;
+  iv: TCryptoLibByteArray;
+  Lparameters: ICipherParameters;
+  diff: Int32;
+begin
+  Fencrypting := forEncryption;
+  Lparameters := parameters;
+
+  if Supports(Lparameters, IParametersWithIV, ivParam) then
+  begin
+    iv := ivParam.GetIV();
+
+    diff := System.length(FIV) - System.length(iv);
+
+    System.Move(iv[0], FIV[diff], System.length(iv) * System.SizeOf(Byte));
+    TArrayUtils.Fill(FIV, 0, diff, Byte(0));
+
+    Lparameters := ivParam.parameters;
+  end;
+
+  Reset();
+
+  // if it's Nil, key is to be reused.
+  if (Lparameters <> Nil) then
+  begin
+    Fcipher.Init(true, Lparameters);
+  end;
+
+end;
+
+function TCfbBlockCipher.ProcessBlock(const input: TCryptoLibByteArray;
+  inOff: Int32; const output: TCryptoLibByteArray; outOff: Int32): Int32;
+begin
+  if Fencrypting then
+  begin
+    result := EncryptBlock(input, inOff, output, outOff);
+  end
+  else
+  begin
+    result := DecryptBlock(input, inOff, output, outOff);
+  end;
+end;
+
+{ TOfbBlockCipher }
+
+constructor TOfbBlockCipher.Create(const cipher: IBlockCipher;
+  blockSize: Int32);
+begin
+  inherited Create();
+  Fcipher := cipher;
+  FblockSize := blockSize div 8;
+
+  System.SetLength(FIV, Fcipher.GetBlockSize);
+  System.SetLength(FofbV, Fcipher.GetBlockSize);
+  System.SetLength(FofbOutV, Fcipher.GetBlockSize);
+end;
+
+procedure TOfbBlockCipher.Reset;
+begin
+  System.Move(FIV[0], FofbV[0], System.length(FIV));
+
+  Fcipher.Reset();
+
+end;
+
+function TOfbBlockCipher.GetAlgorithmName: String;
+begin
+  result := Fcipher.AlgorithmName + '/OFB' + IntToStr(FblockSize * 8);
+end;
+
+function TOfbBlockCipher.GetBlockSize: Int32;
+begin
+  result := FblockSize;
+end;
+
+function TOfbBlockCipher.GetIsPartialBlockOkay: Boolean;
+begin
+  result := true;
+end;
+
+function TOfbBlockCipher.GetUnderlyingCipher: IBlockCipher;
+begin
+  result := Fcipher;
+end;
+
+procedure TOfbBlockCipher.Init(forEncryption: Boolean;
+  // forEncryption ignored by this OFB mode
+  const parameters: ICipherParameters);
+var
+  ivParam: IParametersWithIV;
+  iv: TCryptoLibByteArray;
+  Lparameters: ICipherParameters;
+  I: Int32;
+begin
+  Fencrypting := forEncryption;
+  Lparameters := parameters;
+
+  if Supports(Lparameters, IParametersWithIV, ivParam) then
+  begin
+    iv := ivParam.GetIV();
+
+    if (System.length(iv) < System.length(FIV)) then
+    begin
+      // prepend the supplied IV with zeros (per FIPS PUB 81)
+      System.Move(iv[0], FIV[System.length(FIV) - System.length(iv)],
+        System.length(iv) * System.SizeOf(Byte));
+
+      for I := 0 to System.Pred(System.length(FIV) - System.length(iv)) do
+      begin
+        FIV[I] := 0;
+      end;
+
+    end
+    else
+    begin
+      System.Move(iv[0], FIV[0], System.length(FIV) * System.SizeOf(Byte));
+    end;
+
+    Lparameters := ivParam.parameters;
+  end;
+
+  Reset();
+
+  // if it's Nil, key is to be reused.
+  if (Lparameters <> Nil) then
+  begin
+    Fcipher.Init(true, Lparameters);
+  end;
+
+end;
+
+function TOfbBlockCipher.ProcessBlock(const input: TCryptoLibByteArray;
+  inOff: Int32; const output: TCryptoLibByteArray; outOff: Int32): Int32;
+var
+  I, count: Int32;
+begin
+  if ((inOff + FblockSize) > System.length(input)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
+  end;
+
+  if ((outOff + FblockSize) > System.length(output)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
+  end;
+
+  Fcipher.ProcessBlock(FofbV, 0, FofbOutV, 0);
+
+  //
+  // XOR the ofbV with the plaintext producing the cipher text (and
+  // the next input block).
+  //
+
+  for I := 0 to System.Pred(FblockSize) do
+  begin
+    output[outOff + I] := Byte(FofbOutV[I] xor input[inOff + I]);
+  end;
+
+  //
+  // change over the input block.
+  //
+  count := (System.length(FofbV) - FblockSize) * System.SizeOf(Byte);
+
+  if count > 0 then
+  begin
+    System.Move(FofbV[FblockSize], FofbV[0], count);
+  end;
+
+  System.Move(FofbOutV[0], FofbV[(System.length(FofbV) - FblockSize)],
+    FblockSize * System.SizeOf(Byte));
+
+  result := FblockSize;
+end;
+
+{ TSicBlockCipher }
+
+constructor TSicBlockCipher.Create(const cipher: IBlockCipher);
+begin
+  inherited Create();
+  Fcipher := cipher;
+  FblockSize := Fcipher.GetBlockSize;
+
+  System.SetLength(Fcounter, FblockSize);
+  System.SetLength(FcounterOut, FblockSize);
+  System.SetLength(FIV, FblockSize);
+end;
+
+procedure TSicBlockCipher.Reset;
+begin
+  TArrayUtils.Fill(Fcounter, 0, System.length(Fcounter), Byte(0));
+  System.Move(FIV[0], Fcounter[0], System.length(FIV) * System.SizeOf(Byte));
+
+  Fcipher.Reset();
+
+end;
+
+function TSicBlockCipher.GetAlgorithmName: String;
+begin
+  result := Fcipher.AlgorithmName + '/SIC';
+end;
+
+function TSicBlockCipher.GetBlockSize: Int32;
+begin
+  result := Fcipher.GetBlockSize();
+end;
+
+function TSicBlockCipher.GetIsPartialBlockOkay: Boolean;
+begin
+  result := true;
+end;
+
+function TSicBlockCipher.GetUnderlyingCipher: IBlockCipher;
+begin
+  result := Fcipher;
+end;
+
+{$IFNDEF _FIXINSIGHT_}
+
+procedure TSicBlockCipher.Init(forEncryption: Boolean;
+  // forEncryption ignored by this CTR mode
+  const parameters: ICipherParameters);
+var
+  ivParam: IParametersWithIV;
+  Lparameters: ICipherParameters;
+  maxCounterSize: Int32;
+begin
+  Lparameters := parameters;
+
+  if Supports(Lparameters, IParametersWithIV, ivParam) then
+  begin
+    FIV := ivParam.GetIV();
+
+    if (FblockSize < System.length(FIV)) then
+    begin
+      raise EArgumentCryptoLibException.CreateResFmt(@SInvalidTooLargeIVLength,
+        [FblockSize]);
+    end;
+
+    maxCounterSize := Min(8, FblockSize div 2);
+
+    if ((FblockSize - System.length(FIV)) > maxCounterSize) then
+    begin
+      raise EArgumentCryptoLibException.CreateResFmt(@SInvalidTooSmallIVLength,
+        [FblockSize - maxCounterSize]);
+    end;
+
+    Lparameters := ivParam.parameters;
+  end
+  else
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidParameterArgument);
+  end;
+
+  // if it's Nil, key is to be reused.
+  if (Lparameters <> Nil) then
+  begin
+    Fcipher.Init(true, Lparameters);
+  end;
+
+  Reset();
+
+end;
+{$ENDIF}
+
+function TSicBlockCipher.ProcessBlock(const input: TCryptoLibByteArray;
+  inOff: Int32; const output: TCryptoLibByteArray; outOff: Int32): Int32;
+var
+  I, J: Int32;
+begin
+
+  if ((inOff + FblockSize) > System.length(input)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
+  end;
+
+  if ((outOff + FblockSize) > System.length(output)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
+  end;
+
+  Fcipher.ProcessBlock(Fcounter, 0, FcounterOut, 0);
+
+  //
+  // XOR the counterOut with the plaintext producing the cipher text
+  //
+  for I := 0 to System.Pred(System.length(FcounterOut)) do
+  begin
+
+    output[outOff + I] := Byte(FcounterOut[I] xor input[inOff + I]);
+  end;
+
+  // Increment the counter
+  J := System.length(Fcounter);
+  System.Dec(J);
+  System.Inc(Fcounter[J]);
+  while ((J >= 0) and (Fcounter[J] = 0)) do
+  begin
+    System.Dec(J);
+    System.Inc(Fcounter[J]);
+  end;
+
+  result := System.length(Fcounter);
+end;
+
+end.

+ 555 - 0
src/libraries/cryptolib4pascal/ClpBlowfishEngine.pas

@@ -0,0 +1,555 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpBlowfishEngine;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpIBlockCipher,
+  ClpIKeyParameter,
+  ClpICipherParameters,
+  ClpIBlowfishEngine,
+  ClpCheck,
+  ClpConverters,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SBlowfishEngineNotInitialised = 'Blowfish Engine not Initialised';
+  SInvalidParameterBlowfishInit =
+    'Invalid Parameter Passed to Blowfish Init - "%s"';
+  SInputBuffertooShort = 'Input Buffer too Short';
+  SOutputBuffertooShort = 'Output Buffer too Short';
+  SInvalidKeyLength =
+    'Key Length must be between 32 - 448 bits and divisible by 8.';
+
+type
+
+  /// <summary>
+  /// A class that provides Blowfish key encryption operations, <br />such as
+  /// encoding data and generating keys. <br />All the algorithms herein are
+  /// from Applied Cryptography <br />and implement a simplified cryptography
+  /// interface. <br />
+  /// </summary>
+  TBlowfishEngine = class sealed(TInterfacedObject, IBlowfishEngine,
+    IBlockCipher)
+
+  strict private
+  const
+    ROUNDS = Int32(16);
+    BLOCK_SIZE = Int32(8); // bytes = 64 bits
+    SBOX_SK = Int32(256);
+    P_SZ = Int32(ROUNDS + 2);
+
+    KP: array [0 .. 17] of UInt32 = ($243F6A88, $85A308D3, $13198A2E, $03707344,
+      $A4093822, $299F31D0, $082EFA98, $EC4E6C89, $452821E6, $38D01377,
+      $BE5466CF, $34E90C6C, $C0AC29B7, $C97C50DD, $3F84D5B5, $B5470917,
+      $9216D5D9, $8979FB1B);
+
+    KS0: array [0 .. 255] of UInt32 = ($D1310BA6, $98DFB5AC, $2FFD72DB,
+      $D01ADFB7, $B8E1AFED, $6A267E96, $BA7C9045, $F12C7F99, $24A19947,
+      $B3916CF7, $0801F2E2, $858EFC16, $636920D8, $71574E69, $A458FEA3,
+      $F4933D7E, $0D95748F, $728EB658, $718BCD58, $82154AEE, $7B54A41D,
+      $C25A59B5, $9C30D539, $2AF26013, $C5D1B023, $286085F0, $CA417918,
+      $B8DB38EF, $8E79DCB0, $603A180E, $6C9E0E8B, $B01E8A3E, $D71577C1,
+      $BD314B27, $78AF2FDA, $55605C60, $E65525F3, $AA55AB94, $57489862,
+      $63E81440, $55CA396A, $2AAB10B6, $B4CC5C34, $1141E8CE, $A15486AF,
+      $7C72E993, $B3EE1411, $636FBC2A, $2BA9C55D, $741831F6, $CE5C3E16,
+      $9B87931E, $AFD6BA33, $6C24CF5C, $7A325381, $28958677, $3B8F4898,
+      $6B4BB9AF, $C4BFE81B, $66282193, $61D809CC, $FB21A991, $487CAC60,
+      $5DEC8032, $EF845D5D, $E98575B1, $DC262302, $EB651B88, $23893E81,
+      $D396ACC5, $0F6D6FF3, $83F44239, $2E0B4482, $A4842004, $69C8F04A,
+      $9E1F9B5E, $21C66842, $F6E96C9A, $670C9C61, $ABD388F0, $6A51A0D2,
+      $D8542F68, $960FA728, $AB5133A3, $6EEF0B6C, $137A3BE4, $BA3BF050,
+      $7EFB2A98, $A1F1651D, $39AF0176, $66CA593E, $82430E88, $8CEE8619,
+      $456F9FB4, $7D84A5C3, $3B8B5EBE, $E06F75D8, $85C12073, $401A449F,
+      $56C16AA6, $4ED3AA62, $363F7706, $1BFEDF72, $429B023D, $37D0D724,
+      $D00A1248, $DB0FEAD3, $49F1C09B, $075372C9, $80991B7B, $25D479D8,
+      $F6E8DEF7, $E3FE501A, $B6794C3B, $976CE0BD, $04C006BA, $C1A94FB6,
+      $409F60C4, $5E5C9EC2, $196A2463, $68FB6FAF, $3E6C53B5, $1339B2EB,
+      $3B52EC6F, $6DFC511F, $9B30952C, $CC814544, $AF5EBD09, $BEE3D004,
+      $DE334AFD, $660F2807, $192E4BB3, $C0CBA857, $45C8740F, $D20B5F39,
+      $B9D3FBDB, $5579C0BD, $1A60320A, $D6A100C6, $402C7279, $679F25FE,
+      $FB1FA3CC, $8EA5E9F8, $DB3222F8, $3C7516DF, $FD616B15, $2F501EC8,
+      $AD0552AB, $323DB5FA, $FD238760, $53317B48, $3E00DF82, $9E5C57BB,
+      $CA6F8CA0, $1A87562E, $DF1769DB, $D542A8F6, $287EFFC3, $AC6732C6,
+      $8C4F5573, $695B27B0, $BBCA58C8, $E1FFA35D, $B8F011A0, $10FA3D98,
+      $FD2183B8, $4AFCB56C, $2DD1D35B, $9A53E479, $B6F84565, $D28E49BC,
+      $4BFB9790, $E1DDF2DA, $A4CB7E33, $62FB1341, $CEE4C6E8, $EF20CADA,
+      $36774C01, $D07E9EFE, $2BF11FB4, $95DBDA4D, $AE909198, $EAAD8E71,
+      $6B93D5A0, $D08ED1D0, $AFC725E0, $8E3C5B2F, $8E7594B7, $8FF6E2FB,
+      $F2122B64, $8888B812, $900DF01C, $4FAD5EA0, $688FC31C, $D1CFF191,
+      $B3A8C1AD, $2F2F2218, $BE0E1777, $EA752DFE, $8B021FA1, $E5A0CC0F,
+      $B56F74E8, $18ACF3D6, $CE89E299, $B4A84FE0, $FD13E0B7, $7CC43B81,
+      $D2ADA8D9, $165FA266, $80957705, $93CC7314, $211A1477, $E6AD2065,
+      $77B5FA86, $C75442F5, $FB9D35CF, $EBCDAF0C, $7B3E89A0, $D6411BD3,
+      $AE1E7E49, $00250E2D, $2071B35E, $226800BB, $57B8E0AF, $2464369B,
+      $F009B91E, $5563911D, $59DFA6AA, $78C14389, $D95A537F, $207D5BA2,
+      $02E5B9C5, $83260376, $6295CFA9, $11C81968, $4E734A41, $B3472DCA,
+      $7B14A94A, $1B510052, $9A532915, $D60F573F, $BC9BC6E4, $2B60A476,
+      $81E67400, $08BA6FB5, $571BE91F, $F296EC6B, $2A0DD915, $B6636521,
+      $E7B9F9B6, $FF34052E, $C5855664, $53B02D5D, $A99F8FA1, $08BA4799,
+      $6E85076A);
+
+    KS1: array [0 .. 255] of UInt32 = ($4B7A70E9, $B5B32944, $DB75092E,
+      $C4192623, $AD6EA6B0, $49A7DF7D, $9CEE60B8, $8FEDB266, $ECAA8C71,
+      $699A17FF, $5664526C, $C2B19EE1, $193602A5, $75094C29, $A0591340,
+      $E4183A3E, $3F54989A, $5B429D65, $6B8FE4D6, $99F73FD6, $A1D29C07,
+      $EFE830F5, $4D2D38E6, $F0255DC1, $4CDD2086, $8470EB26, $6382E9C6,
+      $021ECC5E, $09686B3F, $3EBAEFC9, $3C971814, $6B6A70A1, $687F3584,
+      $52A0E286, $B79C5305, $AA500737, $3E07841C, $7FDEAE5C, $8E7D44EC,
+      $5716F2B8, $B03ADA37, $F0500C0D, $F01C1F04, $0200B3FF, $AE0CF51A,
+      $3CB574B2, $25837A58, $DC0921BD, $D19113F9, $7CA92FF6, $94324773,
+      $22F54701, $3AE5E581, $37C2DADC, $C8B57634, $9AF3DDA7, $A9446146,
+      $0FD0030E, $ECC8C73E, $A4751E41, $E238CD99, $3BEA0E2F, $3280BBA1,
+      $183EB331, $4E548B38, $4F6DB908, $6F420D03, $F60A04BF, $2CB81290,
+      $24977C79, $5679B072, $BCAF89AF, $DE9A771F, $D9930810, $B38BAE12,
+      $DCCF3F2E, $5512721F, $2E6B7124, $501ADDE6, $9F84CD87, $7A584718,
+      $7408DA17, $BC9F9ABC, $E94B7D8C, $EC7AEC3A, $DB851DFA, $63094366,
+      $C464C3D2, $EF1C1847, $3215D908, $DD433B37, $24C2BA16, $12A14D43,
+      $2A65C451, $50940002, $133AE4DD, $71DFF89E, $10314E55, $81AC77D6,
+      $5F11199B, $043556F1, $D7A3C76B, $3C11183B, $5924A509, $F28FE6ED,
+      $97F1FBFA, $9EBABF2C, $1E153C6E, $86E34570, $EAE96FB1, $860E5E0A,
+      $5A3E2AB3, $771FE71C, $4E3D06FA, $2965DCB9, $99E71D0F, $803E89D6,
+      $5266C825, $2E4CC978, $9C10B36A, $C6150EBA, $94E2EA78, $A5FC3C53,
+      $1E0A2DF4, $F2F74EA7, $361D2B3D, $1939260F, $19C27960, $5223A708,
+      $F71312B6, $EBADFE6E, $EAC31F66, $E3BC4595, $A67BC883, $B17F37D1,
+      $018CFF28, $C332DDEF, $BE6C5AA5, $65582185, $68AB9802, $EECEA50F,
+      $DB2F953B, $2AEF7DAD, $5B6E2F84, $1521B628, $29076170, $ECDD4775,
+      $619F1510, $13CCA830, $EB61BD96, $0334FE1E, $AA0363CF, $B5735C90,
+      $4C70A239, $D59E9E0B, $CBAADE14, $EECC86BC, $60622CA7, $9CAB5CAB,
+      $B2F3846E, $648B1EAF, $19BDF0CA, $A02369B9, $655ABB50, $40685A32,
+      $3C2AB4B3, $319EE9D5, $C021B8F7, $9B540B19, $875FA099, $95F7997E,
+      $623D7DA8, $F837889A, $97E32D77, $11ED935F, $16681281, $0E358829,
+      $C7E61FD6, $96DEDFA1, $7858BA99, $57F584A5, $1B227263, $9B83C3FF,
+      $1AC24696, $CDB30AEB, $532E3054, $8FD948E4, $6DBC3128, $58EBF2EF,
+      $34C6FFEA, $FE28ED61, $EE7C3C73, $5D4A14D9, $E864B7E3, $42105D14,
+      $203E13E0, $45EEE2B6, $A3AAABEA, $DB6C4F15, $FACB4FD0, $C742F442,
+      $EF6ABBB5, $654F3B1D, $41CD2105, $D81E799E, $86854DC7, $E44B476A,
+      $3D816250, $CF62A1F2, $5B8D2646, $FC8883A0, $C1C7B6A3, $7F1524C3,
+      $69CB7492, $47848A0B, $5692B285, $095BBF00, $AD19489D, $1462B174,
+      $23820E00, $58428D2A, $0C55F5EA, $1DADF43E, $233F7061, $3372F092,
+      $8D937E41, $D65FECF1, $6C223BDB, $7CDE3759, $CBEE7460, $4085F2A7,
+      $CE77326E, $A6078084, $19F8509E, $E8EFD855, $61D99735, $A969A7AA,
+      $C50C06C2, $5A04ABFC, $800BCADC, $9E447A2E, $C3453484, $FDD56705,
+      $0E1E9EC9, $DB73DBD3, $105588CD, $675FDA79, $E3674340, $C5C43465,
+      $713E38D8, $3D28F89E, $F16DFF20, $153E21E7, $8FB03D4A, $E6E39F2B,
+      $DB83ADF7);
+
+    KS2: array [0 .. 255] of UInt32 = ($E93D5A68, $948140F7, $F64C261C,
+      $94692934, $411520F7, $7602D4F7, $BCF46B2E, $D4A20068, $D4082471,
+      $3320F46A, $43B7D4B7, $500061AF, $1E39F62E, $97244546, $14214F74,
+      $BF8B8840, $4D95FC1D, $96B591AF, $70F4DDD3, $66A02F45, $BFBC09EC,
+      $03BD9785, $7FAC6DD0, $31CB8504, $96EB27B3, $55FD3941, $DA2547E6,
+      $ABCA0A9A, $28507825, $530429F4, $0A2C86DA, $E9B66DFB, $68DC1462,
+      $D7486900, $680EC0A4, $27A18DEE, $4F3FFEA2, $E887AD8C, $B58CE006,
+      $7AF4D6B6, $AACE1E7C, $D3375FEC, $CE78A399, $406B2A42, $20FE9E35,
+      $D9F385B9, $EE39D7AB, $3B124E8B, $1DC9FAF7, $4B6D1856, $26A36631,
+      $EAE397B2, $3A6EFA74, $DD5B4332, $6841E7F7, $CA7820FB, $FB0AF54E,
+      $D8FEB397, $454056AC, $BA489527, $55533A3A, $20838D87, $FE6BA9B7,
+      $D096954B, $55A867BC, $A1159A58, $CCA92963, $99E1DB33, $A62A4A56,
+      $3F3125F9, $5EF47E1C, $9029317C, $FDF8E802, $04272F70, $80BB155C,
+      $05282CE3, $95C11548, $E4C66D22, $48C1133F, $C70F86DC, $07F9C9EE,
+      $41041F0F, $404779A4, $5D886E17, $325F51EB, $D59BC0D1, $F2BCC18F,
+      $41113564, $257B7834, $602A9C60, $DFF8E8A3, $1F636C1B, $0E12B4C2,
+      $02E1329E, $AF664FD1, $CAD18115, $6B2395E0, $333E92E1, $3B240B62,
+      $EEBEB922, $85B2A20E, $E6BA0D99, $DE720C8C, $2DA2F728, $D0127845,
+      $95B794FD, $647D0862, $E7CCF5F0, $5449A36F, $877D48FA, $C39DFD27,
+      $F33E8D1E, $0A476341, $992EFF74, $3A6F6EAB, $F4F8FD37, $A812DC60,
+      $A1EBDDF8, $991BE14C, $DB6E6B0D, $C67B5510, $6D672C37, $2765D43B,
+      $DCD0E804, $F1290DC7, $CC00FFA3, $B5390F92, $690FED0B, $667B9FFB,
+      $CEDB7D9C, $A091CF0B, $D9155EA3, $BB132F88, $515BAD24, $7B9479BF,
+      $763BD6EB, $37392EB3, $CC115979, $8026E297, $F42E312D, $6842ADA7,
+      $C66A2B3B, $12754CCC, $782EF11C, $6A124237, $B79251E7, $06A1BBE6,
+      $4BFB6350, $1A6B1018, $11CAEDFA, $3D25BDD8, $E2E1C3C9, $44421659,
+      $0A121386, $D90CEC6E, $D5ABEA2A, $64AF674E, $DA86A85F, $BEBFE988,
+      $64E4C3FE, $9DBC8057, $F0F7C086, $60787BF8, $6003604D, $D1FD8346,
+      $F6381FB0, $7745AE04, $D736FCCC, $83426B33, $F01EAB71, $B0804187,
+      $3C005E5F, $77A057BE, $BDE8AE24, $55464299, $BF582E61, $4E58F48F,
+      $F2DDFDA2, $F474EF38, $8789BDC2, $5366F9C3, $C8B38E74, $B475F255,
+      $46FCD9B9, $7AEB2661, $8B1DDF84, $846A0E79, $915F95E2, $466E598E,
+      $20B45770, $8CD55591, $C902DE4C, $B90BACE1, $BB8205D0, $11A86248,
+      $7574A99E, $B77F19B6, $E0A9DC09, $662D09A1, $C4324633, $E85A1F02,
+      $09F0BE8C, $4A99A025, $1D6EFE10, $1AB93D1D, $0BA5A4DF, $A186F20F,
+      $2868F169, $DCB7DA83, $573906FE, $A1E2CE9B, $4FCD7F52, $50115E01,
+      $A70683FA, $A002B5C4, $0DE6D027, $9AF88C27, $773F8641, $C3604C06,
+      $61A806B5, $F0177A28, $C0F586E0, $006058AA, $30DC7D62, $11E69ED7,
+      $2338EA63, $53C2DD94, $C2C21634, $BBCBEE56, $90BCB6DE, $EBFC7DA1,
+      $CE591D76, $6F05E409, $4B7C0188, $39720A3D, $7C927C24, $86E3725F,
+      $724D9DB9, $1AC15BB4, $D39EB8FC, $ED545578, $08FCA5B5, $D83D7CD3,
+      $4DAD0FC4, $1E50EF5E, $B161E6F8, $A28514D9, $6C51133C, $6FD5C7E7,
+      $56E14EC4, $362ABFCE, $DDC6C837, $D79A3234, $92638212, $670EFA8E,
+      $406000E0);
+
+    KS3: array [0 .. 255] of UInt32 = ($3A39CE37, $D3FAF5CF, $ABC27737,
+      $5AC52D1B, $5CB0679E, $4FA33742, $D3822740, $99BC9BBE, $D5118E9D,
+      $BF0F7315, $D62D1C7E, $C700C47B, $B78C1B6B, $21A19045, $B26EB1BE,
+      $6A366EB4, $5748AB2F, $BC946E79, $C6A376D2, $6549C2C8, $530FF8EE,
+      $468DDE7D, $D5730A1D, $4CD04DC6, $2939BBDB, $A9BA4650, $AC9526E8,
+      $BE5EE304, $A1FAD5F0, $6A2D519A, $63EF8CE2, $9A86EE22, $C089C2B8,
+      $43242EF6, $A51E03AA, $9CF2D0A4, $83C061BA, $9BE96A4D, $8FE51550,
+      $BA645BD6, $2826A2F9, $A73A3AE1, $4BA99586, $EF5562E9, $C72FEFD3,
+      $F752F7DA, $3F046F69, $77FA0A59, $80E4A915, $87B08601, $9B09E6AD,
+      $3B3EE593, $E990FD5A, $9E34D797, $2CF0B7D9, $022B8B51, $96D5AC3A,
+      $017DA67D, $D1CF3ED6, $7C7D2D28, $1F9F25CF, $ADF2B89B, $5AD6B472,
+      $5A88F54C, $E029AC71, $E019A5E6, $47B0ACFD, $ED93FA9B, $E8D3C48D,
+      $283B57CC, $F8D56629, $79132E28, $785F0191, $ED756055, $F7960E44,
+      $E3D35E8C, $15056DD4, $88F46DBA, $03A16125, $0564F0BD, $C3EB9E15,
+      $3C9057A2, $97271AEC, $A93A072A, $1B3F6D9B, $1E6321F5, $F59C66FB,
+      $26DCF319, $7533D928, $B155FDF5, $03563482, $8ABA3CBB, $28517711,
+      $C20AD9F8, $ABCC5167, $CCAD925F, $4DE81751, $3830DC8E, $379D5862,
+      $9320F991, $EA7A90C2, $FB3E7BCE, $5121CE64, $774FBE32, $A8B6E37E,
+      $C3293D46, $48DE5369, $6413E680, $A2AE0810, $DD6DB224, $69852DFD,
+      $09072166, $B39A460A, $6445C0DD, $586CDECF, $1C20C8AE, $5BBEF7DD,
+      $1B588D40, $CCD2017F, $6BB4E3BB, $DDA26A7E, $3A59FF45, $3E350A44,
+      $BCB4CDD5, $72EACEA8, $FA6484BB, $8D6612AE, $BF3C6F47, $D29BE463,
+      $542F5D9E, $AEC2771B, $F64E6370, $740E0D8D, $E75B1357, $F8721671,
+      $AF537D5D, $4040CB08, $4EB4E2CC, $34D2466A, $0115AF84, $E1B00428,
+      $95983A1D, $06B89FB4, $CE6EA048, $6F3F3B82, $3520AB82, $011A1D4B,
+      $277227F8, $611560B1, $E7933FDC, $BB3A792B, $344525BD, $A08839E1,
+      $51CE794B, $2F32C9B7, $A01FBAC9, $E01CC87E, $BCC7D1F6, $CF0111C3,
+      $A1E8AAC7, $1A908749, $D44FBD9A, $D0DADECB, $D50ADA38, $0339C32A,
+      $C6913667, $8DF9317C, $E0B12B4F, $F79E59B7, $43F5BB3A, $F2D519FF,
+      $27D9459C, $BF97222C, $15E6FC2A, $0F91FC71, $9B941525, $FAE59361,
+      $CEB69CEB, $C2A86459, $12BAA8D1, $B6C1075E, $E3056A0C, $10D25065,
+      $CB03A442, $E0EC6E0E, $1698DB3B, $4C98A0BE, $3278E964, $9F1F9532,
+      $E0D392DF, $D3A0342B, $8971F21E, $1B0A7441, $4BA3348C, $C5BE7120,
+      $C37632D8, $DF359F8D, $9B992F2E, $E60B6F47, $0FE3F11D, $E54CDA54,
+      $1EDAD891, $CE6279CF, $CD3E7E6F, $1618B166, $FD2C1D05, $848FD2C5,
+      $F6FB2299, $F523F357, $A6327623, $93A83531, $56CCCD02, $ACF08162,
+      $5A75EBB5, $6E163697, $88D273CC, $DE966292, $81B949D0, $4C50901B,
+      $71C65614, $E6C6C7BD, $327A140A, $45E1D006, $C3F27B9A, $C9AA53FD,
+      $62A80F00, $BB25BFE2, $35BDD2F6, $71126905, $B2040222, $B6CBCF7C,
+      $CD769C2B, $53113EC0, $1640E3D3, $38ABBD60, $2547ADF0, $BA38209C,
+      $F746CE76, $77AFA1C5, $20756060, $85CBFE4E, $8AE88DD8, $7AAAF9B0,
+      $4CF9AA7E, $1948C25C, $02FB8A8C, $01C36AE4, $D6EBE1F9, $90D4F869,
+      $A65CDEA0, $3F09252D, $C208E69F, $B74E6132, $CE77E25B, $578FDFE3,
+      $3AC372E6);
+
+  var
+    FS0, FS1, FS2, FS3: array [0 .. (SBOX_SK - 1)] of UInt32; // the s-boxes
+    FP: array [0 .. (P_SZ - 1)] of UInt32; // the p-array
+
+    FforEncryption: Boolean;
+
+    FWorkingKey: TCryptoLibByteArray;
+
+    function F(x: UInt32): UInt32; inline;
+    procedure SetKey(const key: TCryptoLibByteArray);
+
+    /// <summary>
+    /// apply the encryption cycle to each value pair in the table.
+    /// </summary>
+    procedure ProcessTable(xl, xr: UInt32; var table: array of UInt32);
+
+    /// <summary>
+    /// Encrypt the given input starting at the given offset and place <br />
+    /// the result in the provided buffer starting at the given offset. <br />
+    /// The input will be an exact multiple of our blocksize.
+    /// </summary>
+    procedure EncryptBlock(const src: TCryptoLibByteArray; srcIndex: Int32;
+      const dst: TCryptoLibByteArray; dstIndex: Int32);
+
+    /// <summary>
+    /// Decrypt the given input starting at the given offset and place <br />
+    /// the result in the provided buffer starting at the given offset. <br />
+    /// The input will be an exact multiple of our blocksize.
+    /// </summary>
+    procedure DecryptBlock(const src: TCryptoLibByteArray; srcIndex: Int32;
+      const dst: TCryptoLibByteArray; dstIndex: Int32);
+
+    function GetAlgorithmName: String; virtual;
+    function GetIsPartialBlockOkay: Boolean; virtual;
+    function GetBlockSize(): Int32; virtual;
+
+  public
+
+    /// <summary>
+    /// initialise a Blowfish cipher.
+    /// </summary>
+    /// <param name="forEncryption">
+    /// whether or not we are for encryption.
+    /// </param>
+    /// <param name="parameters">
+    /// the parameters required to set up the cipher.
+    /// </param>
+    /// <exception cref="EArgumentCryptoLibException">
+    /// if the parameters argument is inappropriate.
+    /// </exception>
+    procedure Init(forEncryption: Boolean;
+      const parameters: ICipherParameters); virtual;
+
+    function ProcessBlock(const input: TCryptoLibByteArray; inOff: Int32;
+      const output: TCryptoLibByteArray; outOff: Int32): Int32; virtual;
+
+    procedure Reset(); virtual;
+
+    property AlgorithmName: String read GetAlgorithmName;
+    property IsPartialBlockOkay: Boolean read GetIsPartialBlockOkay;
+
+  end;
+
+implementation
+
+{ TBlowfishEngine }
+
+function TBlowfishEngine.F(x: UInt32): UInt32;
+begin
+  result := (((FS0[x shr 24] + FS1[(x shr 16) and $FF]) xor FS2[(x shr 8) and
+    $FF]) + FS3[x and $FF]);
+end;
+
+procedure TBlowfishEngine.SetKey(const key: TCryptoLibByteArray);
+var
+  keyLength, keyIndex, i, j: Int32;
+  data: UInt32;
+begin
+  keyLength := System.Length(key);
+  if ((keyLength < 4) or (keyLength > 56) or (((keyLength * 8) and 7) <> 0))
+  then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidKeyLength);
+  end;
+
+  (*
+    * - comments are from _Applied Crypto_, Schneier, p338
+    * please be careful comparing the two, AC numbers the
+    * arrays from 1, the enclosed code from 0.
+    *
+    * (1)
+    * Initialise the S-boxes and the P-array, with a fixed string
+    * This string contains the hexadecimal digits of pi (3.141...)
+  *)
+  System.Move(KS0[System.Low(KS0)], FS0[System.Low(FS0)],
+    SBOX_SK * System.SizeOf(UInt32));
+  System.Move(KS1[System.Low(KS1)], FS1[System.Low(FS1)],
+    SBOX_SK * System.SizeOf(UInt32));
+  System.Move(KS2[System.Low(KS2)], FS2[System.Low(FS2)],
+    SBOX_SK * System.SizeOf(UInt32));
+  System.Move(KS3[System.Low(KS3)], FS3[System.Low(FS3)],
+    SBOX_SK * System.SizeOf(UInt32));
+
+  System.Move(KP[System.Low(KP)], FP[System.Low(FP)],
+    P_SZ * System.SizeOf(UInt32));
+
+  (*
+    * (2)
+    * Now, XOR P[0] with the first 32 bits of the key, XOR P[1] with the
+    * second 32-bits of the key, and so on for all bits of the key
+    * (up to P[17]).  Repeatedly cycle through the key bits until the
+    * entire P-array has been XOR-ed with the key bits
+  *)
+  keyIndex := 0;
+
+  i := 0;
+  while i < P_SZ do
+  begin
+    // Get the 32 bits of the key, in 4 * 8 bit chunks
+    data := $0000000;
+    j := 0;
+    while j < 4 do
+    begin
+      // create a 32 bit block
+      data := (data shl 8) or UInt32(key[keyIndex]);
+      System.Inc(keyIndex);
+
+      // wrap when we get to the end of the key
+      if (keyIndex >= keyLength) then
+      begin
+        keyIndex := 0;
+      end;
+      System.Inc(j);
+    end;
+    // XOR the newly created 32 bit chunk onto the P-array
+    FP[i] := FP[i] xor data;
+
+    System.Inc(i);
+  end;
+
+  (*
+    * (3)
+    * Encrypt the all-zero string with the Blowfish algorithm, using
+    * the subkeys described in (1) and (2)
+    *
+    * (4)
+    * Replace P1 and P2 with the output of step (3)
+    *
+    * (5)
+    * Encrypt the output of step(3) using the Blowfish algorithm,
+    * with the modified subkeys.
+    *
+    * (6)
+    * Replace P3 and P4 with the output of step (5)
+    *
+    * (7)
+    * Continue the process, replacing all elements of the P-array
+    * and then all four S-boxes in order, with the output of the
+    * continuously changing Blowfish algorithm
+  *)
+
+  ProcessTable(0, 0, FP);
+  ProcessTable(FP[P_SZ - 2], FP[P_SZ - 1], FS0);
+  ProcessTable(FS0[SBOX_SK - 2], FS0[SBOX_SK - 1], FS1);
+  ProcessTable(FS1[SBOX_SK - 2], FS1[SBOX_SK - 1], FS2);
+  ProcessTable(FS2[SBOX_SK - 2], FS2[SBOX_SK - 1], FS3);
+end;
+
+procedure TBlowfishEngine.EncryptBlock(const src: TCryptoLibByteArray;
+  srcIndex: Int32; const dst: TCryptoLibByteArray; dstIndex: Int32);
+var
+  xl, xr: UInt32;
+  i: Int32;
+begin
+  xl := TConverters.ReadBytesAsUInt32BE(PByte(src), srcIndex);
+  xr := TConverters.ReadBytesAsUInt32BE(PByte(src), srcIndex + 4);
+
+  xl := xl xor FP[0];
+
+  i := 1;
+  while i < ROUNDS do
+
+  begin
+    xr := xr xor (F(xl) xor FP[i]);
+    xl := xl xor (F(xr) xor FP[i + 1]);
+    System.Inc(i, 2);
+  end;
+
+  xr := xr xor FP[ROUNDS + 1];
+
+  TConverters.ReadUInt32AsBytesBE(xr, dst, dstIndex);
+  TConverters.ReadUInt32AsBytesBE(xl, dst, dstIndex + 4);
+end;
+
+procedure TBlowfishEngine.DecryptBlock(const src: TCryptoLibByteArray;
+  srcIndex: Int32; const dst: TCryptoLibByteArray; dstIndex: Int32);
+var
+  xl, xr: UInt32;
+  i: Int32;
+begin
+  xl := TConverters.ReadBytesAsUInt32BE(PByte(src), srcIndex);
+  xr := TConverters.ReadBytesAsUInt32BE(PByte(src), srcIndex + 4);
+
+  xl := xl xor FP[ROUNDS + 1];
+
+  i := ROUNDS;
+  while i > 0 do
+
+  begin
+    xr := xr xor (F(xl) xor FP[i]);
+    xl := xl xor (F(xr) xor FP[i - 1]);
+    System.Dec(i, 2);
+  end;
+
+  xr := xr xor FP[0];
+
+  TConverters.ReadUInt32AsBytesBE(xr, dst, dstIndex);
+  TConverters.ReadUInt32AsBytesBE(xl, dst, dstIndex + 4);
+end;
+
+function TBlowfishEngine.GetAlgorithmName: String;
+begin
+  result := 'Blowfish';
+end;
+
+function TBlowfishEngine.GetBlockSize: Int32;
+begin
+  result := BLOCK_SIZE;
+end;
+
+function TBlowfishEngine.GetIsPartialBlockOkay: Boolean;
+begin
+  result := false;
+end;
+
+procedure TBlowfishEngine.Init(forEncryption: Boolean;
+  const parameters: ICipherParameters);
+var
+  keyParameter: IKeyParameter;
+begin
+  if not Supports(parameters, IKeyParameter, keyParameter) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt
+      (@SInvalidParameterBlowfishInit, [(parameters as TObject).ToString]);
+  end;
+  FforEncryption := forEncryption;
+  FWorkingKey := keyParameter.GetKey();
+  SetKey(FWorkingKey);
+end;
+
+function TBlowfishEngine.ProcessBlock(const input: TCryptoLibByteArray;
+  inOff: Int32; const output: TCryptoLibByteArray; outOff: Int32): Int32;
+begin
+
+  if (FWorkingKey = Nil) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SBlowfishEngineNotInitialised);
+  end;
+
+  TCheck.DataLength(input, inOff, BLOCK_SIZE, SInputBuffertooShort);
+  TCheck.OutputLength(output, outOff, BLOCK_SIZE, SOutputBuffertooShort);
+
+  if (FforEncryption) then
+  begin
+    EncryptBlock(input, inOff, output, outOff);
+  end
+  else
+  begin
+    DecryptBlock(input, inOff, output, outOff);
+  end;
+
+  result := BLOCK_SIZE;
+end;
+
+procedure TBlowfishEngine.ProcessTable(xl, xr: UInt32;
+  var table: array of UInt32);
+var
+  size, s, i: Int32;
+begin
+  size := System.Length(table);
+  s := 0;
+  while s < size do
+  begin
+    xl := xl xor FP[0];
+    i := 1;
+    while i < ROUNDS do
+    begin
+      xr := xr xor (F(xl) xor FP[i]);
+      xl := xl xor (F(xr) xor FP[i + 1]);
+      System.Inc(i, 2);
+    end;
+
+    xr := xr xor FP[ROUNDS + 1];
+
+    table[s] := xr;
+    table[s + 1] := xl;
+
+    xr := xl; // end of cycle swap
+    xl := table[s];
+    System.Inc(s, 2);
+  end;
+end;
+
+procedure TBlowfishEngine.Reset;
+begin
+  // nothing to do.
+end;
+
+end.

+ 172 - 0
src/libraries/cryptolib4pascal/ClpBsiObjectIdentifiers.pas

@@ -0,0 +1,172 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpBsiObjectIdentifiers;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpAsn1Objects,
+  ClpIAsn1Objects;
+
+type
+  TBsiObjectIdentifiers = class abstract(TObject)
+
+  strict private
+
+    /// <remarks>See https://www.bsi.bund.de/cae/servlet/contentblob/471398/publicationFile/30615/BSI-TR-03111_pdf.pdf</remarks>
+  class var
+
+    FIsBooted: Boolean;
+    Fbsi_de, Fid_ecc, Fecdsa_plain_signatures, Fecdsa_plain_SHA1,
+      Fecdsa_plain_SHA224, Fecdsa_plain_SHA256, Fecdsa_plain_SHA384,
+      Fecdsa_plain_SHA512, Fecdsa_plain_RIPEMD160: IDerObjectIdentifier;
+
+    class function Getbsi_de: IDerObjectIdentifier; static; inline;
+    class function Getecdsa_plain_RIPEMD160: IDerObjectIdentifier;
+      static; inline;
+    class function Getecdsa_plain_SHA1: IDerObjectIdentifier; static; inline;
+    class function Getecdsa_plain_SHA224: IDerObjectIdentifier; static; inline;
+    class function Getecdsa_plain_SHA256: IDerObjectIdentifier; static; inline;
+    class function Getecdsa_plain_SHA384: IDerObjectIdentifier; static; inline;
+    class function Getecdsa_plain_SHA512: IDerObjectIdentifier; static; inline;
+    class function Getecdsa_plain_signatures: IDerObjectIdentifier;
+      static; inline;
+    class function Getid_ecc: IDerObjectIdentifier; static; inline;
+
+    class constructor BsiObjectIdentifiers();
+
+  public
+
+    class property bsi_de: IDerObjectIdentifier read Getbsi_de;
+    class property id_ecc: IDerObjectIdentifier read Getid_ecc;
+    class property ecdsa_plain_signatures: IDerObjectIdentifier
+      read Getecdsa_plain_signatures;
+    class property ecdsa_plain_SHA1: IDerObjectIdentifier
+      read Getecdsa_plain_SHA1;
+    class property ecdsa_plain_SHA224: IDerObjectIdentifier
+      read Getecdsa_plain_SHA224;
+    class property ecdsa_plain_SHA256: IDerObjectIdentifier
+      read Getecdsa_plain_SHA256;
+    class property ecdsa_plain_SHA384: IDerObjectIdentifier
+      read Getecdsa_plain_SHA384;
+    class property ecdsa_plain_SHA512: IDerObjectIdentifier
+      read Getecdsa_plain_SHA512;
+    class property ecdsa_plain_RIPEMD160: IDerObjectIdentifier
+      read Getecdsa_plain_RIPEMD160;
+
+    class procedure Boot(); static;
+
+  end;
+
+implementation
+
+{ TBsiObjectIdentifiers }
+
+class procedure TBsiObjectIdentifiers.Boot;
+begin
+  if not FIsBooted then
+  begin
+    Fbsi_de := TDerObjectIdentifier.Create('0.4.0.127.0.7');
+
+    // /* 0.4.0.127.0.7.1.1 */
+    Fid_ecc := Fbsi_de.Branch('1.1');
+
+    // /* 0.4.0.127.0.7.1.1.4.1 */
+    Fecdsa_plain_signatures := Fid_ecc.Branch('4.1');
+
+    // /* 0.4.0.127.0.7.1.1.4.1.1 */
+    Fecdsa_plain_SHA1 := Fecdsa_plain_signatures.Branch('1');
+
+    // /* 0.4.0.127.0.7.1.1.4.1.2 */
+    Fecdsa_plain_SHA224 := Fecdsa_plain_signatures.Branch('2');
+
+    // /* 0.4.0.127.0.7.1.1.4.1.3 */
+    Fecdsa_plain_SHA256 := Fecdsa_plain_signatures.Branch('3');
+
+    // /* 0.4.0.127.0.7.1.1.4.1.4 */
+    Fecdsa_plain_SHA384 := Fecdsa_plain_signatures.Branch('4');
+
+    // /* 0.4.0.127.0.7.1.1.4.1.5 */
+    Fecdsa_plain_SHA512 := Fecdsa_plain_signatures.Branch('5');
+
+    // /* 0.4.0.127.0.7.1.1.4.1.6 */
+    Fecdsa_plain_RIPEMD160 := Fecdsa_plain_signatures.Branch('6');
+
+    FIsBooted := True;
+  end;
+end;
+
+class constructor TBsiObjectIdentifiers.BsiObjectIdentifiers;
+begin
+  TBsiObjectIdentifiers.Boot;
+end;
+
+class function TBsiObjectIdentifiers.Getbsi_de: IDerObjectIdentifier;
+begin
+  result := Fbsi_de;
+end;
+
+class function TBsiObjectIdentifiers.Getecdsa_plain_RIPEMD160
+  : IDerObjectIdentifier;
+begin
+  result := Fecdsa_plain_RIPEMD160;
+end;
+
+class function TBsiObjectIdentifiers.Getecdsa_plain_SHA1: IDerObjectIdentifier;
+begin
+  result := Fecdsa_plain_SHA1;
+end;
+
+class function TBsiObjectIdentifiers.Getecdsa_plain_SHA224
+  : IDerObjectIdentifier;
+begin
+  result := Fecdsa_plain_SHA224;
+end;
+
+class function TBsiObjectIdentifiers.Getecdsa_plain_SHA256
+  : IDerObjectIdentifier;
+begin
+  result := Fecdsa_plain_SHA256;
+end;
+
+class function TBsiObjectIdentifiers.Getecdsa_plain_SHA384
+  : IDerObjectIdentifier;
+begin
+  result := Fecdsa_plain_SHA384;
+end;
+
+class function TBsiObjectIdentifiers.Getecdsa_plain_SHA512
+  : IDerObjectIdentifier;
+begin
+  result := Fecdsa_plain_SHA512;
+end;
+
+class function TBsiObjectIdentifiers.Getecdsa_plain_signatures
+  : IDerObjectIdentifier;
+begin
+  result := Fecdsa_plain_signatures;
+end;
+
+class function TBsiObjectIdentifiers.Getid_ecc: IDerObjectIdentifier;
+begin
+  result := Fid_ecc;
+end;
+
+end.

+ 549 - 0
src/libraries/cryptolib4pascal/ClpBufferedBlockCipher.pas

@@ -0,0 +1,549 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpBufferedBlockCipher;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpCheck,
+  ClpBufferedCipherBase,
+  ClpIBlockCipher,
+  ClpIBufferedBlockCipher,
+  ClpICipherParameters,
+  ClpIParametersWithRandom,
+  ClpArrayUtils,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SInvalidLength = 'Can''t Have a Negative Input Length!';
+  SInputNil = 'Input Cannot be Nil';
+  SCipherNil = 'Cipher Cannot be Nil';
+  SOutputBufferTooSmall = 'Output Buffer too Short';
+  SDataNotBlockSizeAligned = 'Data not Block Size Aligned';
+  SOutputBufferTooSmallForDoFinal = 'Output Buffer too Short for DoFinal()';
+
+type
+
+  /// <summary>
+  /// <para>
+  /// A wrapper class that allows block ciphers to be used to process
+  /// data in a piecemeal fashion. The BufferedBlockCipher outputs a
+  /// block only when the buffer is full and more data is being added, or
+  /// on a doFinal.
+  /// </para>
+  /// <para>
+  /// Note: in the case where the underlying cipher is either a CFB
+  /// cipher or an OFB one the last block may not be a multiple of the
+  /// block size.
+  /// </para>
+  /// </summary>
+  TBufferedBlockCipher = class(TBufferedCipherBase, IBufferedBlockCipher)
+
+  strict protected
+  var
+    Fbuf: TCryptoLibByteArray;
+    FbufOff: Int32;
+    FforEncryption: Boolean;
+    Fcipher: IBlockCipher;
+
+    /// <summary>
+    /// constructor for subclasses
+    /// </summary>
+    constructor Create(); overload;
+
+  public
+    /// <summary>
+    /// Create a buffered block cipher without padding.
+    /// </summary>
+    /// <param name="cipher">
+    /// the underlying block cipher this buffering object wraps.
+    /// </param>
+    constructor Create(const cipher: IBlockCipher); overload;
+
+    /// <summary>
+    /// initialise the cipher.
+    /// </summary>
+    /// <param name="forEncryption">
+    /// forEncryption if true the cipher is initialised for encryption, if
+    /// false for decryption.
+    /// </param>
+    /// <param name="parameters">
+    /// the key and other data required by the cipher.
+    /// </param>
+    /// <exception cref="EArgumentCryptoLibException">
+    /// if the parameters argument is inappropriate.
+    /// </exception>
+    // Note: This doubles as the Init in the event that this cipher is being used as an IWrapper
+    procedure Init(forEncryption: Boolean;
+      const parameters: ICipherParameters); override;
+
+    /// <summary>
+    /// return the blocksize for the underlying cipher.
+    /// </summary>
+    /// <returns>
+    /// return the blocksize for the underlying cipher.
+    /// </returns>
+    function GetBlockSize(): Int32; override;
+
+    /// <summary>
+    /// return the size of the output buffer required for an update an input
+    /// of len bytes.
+    /// </summary>
+    /// <param name="length">
+    /// the length of the input.
+    /// </param>
+    /// <returns>
+    /// return the space required to accommodate a call to update with length
+    /// bytes of input.
+    /// </returns>
+    function GetUpdateOutputSize(length: Int32): Int32; override;
+
+    /// <summary>
+    /// return the size of the output buffer required for an update plus a
+    /// doFinal with an input of length bytes.
+    /// </summary>
+    /// <param name="length">
+    /// the length of the input.
+    /// </param>
+    /// <returns>
+    /// the space required to accommodate a call to update and doFinal with
+    /// length bytes of input.
+    /// </returns>
+    function GetOutputSize(length: Int32): Int32; override;
+
+    /// <summary>
+    /// process a single byte, producing an output block if necessary.
+    /// </summary>
+    /// <param name="input">
+    /// the input byte.
+    /// </param>
+    /// <param name="output">
+    /// the space for any output that might be produced.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset from which the output will be copied.
+    /// </param>
+    /// <returns>
+    /// the number of output bytes copied to output.
+    /// </returns>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if there isn't enough space in output.
+    /// </exception>
+    /// <exception cref="EInvalidOperationCryptoLibException">
+    /// if the cipher isn't initialised.
+    /// </exception>
+    function ProcessByte(input: Byte; const output: TCryptoLibByteArray;
+      outOff: Int32): Int32; overload; override;
+
+    function ProcessByte(input: Byte): TCryptoLibByteArray; overload; override;
+
+    function ProcessBytes(const input: TCryptoLibByteArray;
+      inOff, length: Int32): TCryptoLibByteArray; overload; override;
+
+    /// <summary>
+    /// process an array of bytes, producing output if necessary.
+    /// </summary>
+    /// <param name="input">
+    /// the input byte array.
+    /// </param>
+    /// <param name="inOff">
+    /// the offset at which the input data starts.
+    /// </param>
+    /// <param name="length">
+    /// the number of bytes to be copied out of the input array.
+    /// </param>
+    /// <param name="output">
+    /// the space for any output that might be produced.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset from which the output will be copied.
+    /// </param>
+    /// <returns>
+    /// the number of output bytes copied to output.
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if there isn't enough space in output.
+    /// </exception>
+    /// <exception cref="EInvalidOperationCryptoLibException">
+    /// if the cipher isn't initialised.
+    /// </exception>
+    function ProcessBytes(const input: TCryptoLibByteArray;
+      inOff, length: Int32; const output: TCryptoLibByteArray; outOff: Int32)
+      : Int32; overload; override;
+
+    function DoFinal(): TCryptoLibByteArray; overload; override;
+    function DoFinal(const input: TCryptoLibByteArray; inOff, inLen: Int32)
+      : TCryptoLibByteArray; overload; override;
+
+    /// <summary>
+    /// Process the last block in the buffer.
+    /// </summary>
+    /// <param name="output">
+    /// the array the block currently being held is copied into.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset at which the copying starts.
+    /// </param>
+    /// <returns>
+    /// the number of output bytes copied to output.
+    /// </returns>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if there is insufficient space in output for the output, or the input
+    /// is not block size aligned and should be.
+    /// </exception>
+    /// <exception cref="EInvalidOperationCryptoLibException">
+    /// if the underlying cipher is not initialised.
+    /// </exception>
+    /// <exception cref="EInvalidCipherTextCryptoLibException">
+    /// if padding is expected and not found.
+    /// </exception>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if the input is not block size aligned.
+    /// </exception>
+    function DoFinal(const output: TCryptoLibByteArray; outOff: Int32): Int32;
+      overload; override;
+
+    /// <summary>
+    /// Reset the buffer and cipher. After resetting the object is in the
+    /// same state as it was after the last init (if there was one).
+    /// </summary>
+    procedure Reset(); override;
+
+    function GetAlgorithmName: String; override;
+    property AlgorithmName: String read GetAlgorithmName;
+
+  end;
+
+implementation
+
+{ TBufferedBlockCipher }
+
+constructor TBufferedBlockCipher.Create(const cipher: IBlockCipher);
+begin
+  Inherited Create();
+  if (cipher = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SCipherNil);
+  end;
+
+  Fcipher := cipher;
+  System.SetLength(Fbuf, cipher.GetBlockSize());
+  FbufOff := 0;
+end;
+
+constructor TBufferedBlockCipher.Create;
+begin
+  Inherited Create();
+end;
+
+function TBufferedBlockCipher.DoFinal(const output: TCryptoLibByteArray;
+  outOff: Int32): Int32;
+begin
+  try
+    if (FbufOff <> 0) then
+    begin
+      TCheck.DataLength(not Fcipher.IsPartialBlockOkay,
+        SDataNotBlockSizeAligned);
+      TCheck.OutputLength(output, outOff, FbufOff,
+        SOutputBufferTooSmallForDoFinal);
+
+      // NB: Can't copy directly, or we may write too much output
+      Fcipher.ProcessBlock(Fbuf, 0, Fbuf, 0);
+      System.Move(Fbuf[0], output[outOff], FbufOff * System.SizeOf(Byte));
+    end;
+
+    result := FbufOff;
+    Exit;
+  finally
+    Reset();
+  end;
+end;
+
+function TBufferedBlockCipher.DoFinal(const input: TCryptoLibByteArray;
+  inOff, inLen: Int32): TCryptoLibByteArray;
+var
+  &length, &pos: Int32;
+  outBytes, tmp: TCryptoLibByteArray;
+begin
+  if (input = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SInputNil);
+  end;
+
+  &length := GetOutputSize(inLen);
+
+  outBytes := EmptyBuffer;
+
+  if (&length > 0) then
+  begin
+    System.SetLength(outBytes, length);
+
+    if (inLen > 0) then
+    begin
+      &pos := ProcessBytes(input, inOff, inLen, outBytes, 0);
+    end
+    else
+    begin
+      &pos := 0;
+    end;
+
+    &pos := &pos + DoFinal(outBytes, &pos);
+
+    if (&pos < System.length(outBytes)) then
+    begin
+      System.SetLength(tmp, &pos);
+      System.Move(outBytes[0], tmp[0], &pos * System.SizeOf(Byte));
+      outBytes := tmp;
+    end
+  end
+  else
+  begin
+    Reset();
+  end;
+
+  result := outBytes;
+end;
+
+function TBufferedBlockCipher.DoFinal: TCryptoLibByteArray;
+var
+  outBytes, tmp: TCryptoLibByteArray;
+  &length, &pos: Int32;
+begin
+  outBytes := EmptyBuffer;
+
+  &length := GetOutputSize(0);
+  if (&length > 0) then
+  begin
+    System.SetLength(outBytes, &length);
+
+    &pos := DoFinal(outBytes, 0);
+    if (&pos < System.length(outBytes)) then
+    begin
+      System.SetLength(tmp, &pos);
+      System.Move(outBytes[0], tmp[0], &pos * System.SizeOf(Byte));
+      outBytes := tmp;
+    end
+  end
+  else
+  begin
+    Reset();
+  end;
+
+  result := outBytes;
+end;
+
+function TBufferedBlockCipher.GetAlgorithmName: String;
+begin
+  result := Fcipher.AlgorithmName;
+end;
+
+function TBufferedBlockCipher.GetBlockSize: Int32;
+begin
+  result := Fcipher.GetBlockSize();
+end;
+
+function TBufferedBlockCipher.GetOutputSize(length: Int32): Int32;
+begin
+  // Note: Can assume IsPartialBlockOkay is true for purposes of this calculation
+  result := length + FbufOff;
+end;
+
+function TBufferedBlockCipher.GetUpdateOutputSize(length: Int32): Int32;
+var
+  total, leftOver: Int32;
+begin
+  total := length + FbufOff;
+  leftOver := total mod System.length(Fbuf);
+  result := total - leftOver;
+end;
+
+procedure TBufferedBlockCipher.Init(forEncryption: Boolean;
+  const parameters: ICipherParameters);
+var
+  pwr: IParametersWithRandom;
+  Lparameters: ICipherParameters;
+begin
+  FforEncryption := forEncryption;
+  Lparameters := parameters;
+
+  if Supports(Lparameters, IParametersWithRandom, pwr) then
+  begin
+    Lparameters := pwr.parameters;
+  end;;
+
+  Reset();
+
+  Fcipher.Init(forEncryption, Lparameters);
+
+end;
+
+function TBufferedBlockCipher.ProcessByte(input: Byte;
+  const output: TCryptoLibByteArray; outOff: Int32): Int32;
+begin
+
+  Fbuf[FbufOff] := input;
+  System.Inc(FbufOff);
+
+  if (FbufOff = System.length(Fbuf)) then
+  begin
+    if ((outOff + System.length(Fbuf)) > System.length(output)) then
+    begin
+      raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooSmall);
+    end;
+
+    FbufOff := 0;
+    result := Fcipher.ProcessBlock(Fbuf, 0, output, outOff);
+    Exit;
+  end;
+
+  result := 0;
+end;
+
+function TBufferedBlockCipher.ProcessByte(input: Byte): TCryptoLibByteArray;
+var
+  outLength, &pos: Int32;
+  outBytes, tmp: TCryptoLibByteArray;
+begin
+  outLength := GetUpdateOutputSize(1);
+
+  if outLength > 0 then
+  begin
+    System.SetLength(outBytes, outLength);
+  end
+  else
+  begin
+    outBytes := Nil;
+  end;
+
+  &pos := ProcessByte(input, outBytes, 0);
+
+  if ((outLength > 0) and (pos < outLength)) then
+  begin
+    System.SetLength(tmp, &pos);
+    System.Move(outBytes[0], tmp[0], &pos * System.SizeOf(Byte));
+
+    outBytes := tmp;
+  end;
+
+  result := outBytes;
+end;
+
+function TBufferedBlockCipher.ProcessBytes(const input: TCryptoLibByteArray;
+  inOff, length: Int32; const output: TCryptoLibByteArray;
+  outOff: Int32): Int32;
+var
+  blockSize, outLength, resultLen, gapLen: Int32;
+begin
+  if (length < 1) then
+  begin
+    if (length < 0) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SInvalidLength);
+    end;
+    result := 0;
+    Exit;
+  end;
+
+  blockSize := GetBlockSize();
+  outLength := GetUpdateOutputSize(length);
+
+  if (outLength > 0) then
+  begin
+    TCheck.OutputLength(output, outOff, outLength, SOutputBufferTooSmall);
+  end;
+
+  resultLen := 0;
+  gapLen := System.length(Fbuf) - FbufOff;
+  if (length > gapLen) then
+  begin
+    System.Move(input[inOff], Fbuf[FbufOff], gapLen * System.SizeOf(Byte));
+    resultLen := resultLen + Fcipher.ProcessBlock(Fbuf, 0, output, outOff);
+    FbufOff := 0;
+    length := length - gapLen;
+    inOff := inOff + gapLen;
+    while (length > System.length(Fbuf)) do
+    begin
+      resultLen := resultLen + Fcipher.ProcessBlock(input, inOff, output,
+        outOff + resultLen);
+      length := length - blockSize;
+      inOff := inOff + blockSize;
+    end;
+  end;
+  System.Move(input[inOff], Fbuf[FbufOff], length * System.SizeOf(Byte));
+  FbufOff := FbufOff + length;
+  if (FbufOff = System.length(Fbuf)) then
+  begin
+    resultLen := resultLen + Fcipher.ProcessBlock(Fbuf, 0, output,
+      outOff + resultLen);
+    FbufOff := 0;
+  end;
+  result := resultLen;
+end;
+
+function TBufferedBlockCipher.ProcessBytes(const input: TCryptoLibByteArray;
+  inOff, length: Int32): TCryptoLibByteArray;
+var
+  outLength, &pos: Int32;
+  outBytes, tmp: TCryptoLibByteArray;
+begin
+  if (input = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SInputNil);
+  end;
+  if (length < 1) then
+  begin
+    result := Nil;
+    Exit;
+  end;
+
+  outLength := GetUpdateOutputSize(length);
+
+  if outLength > 0 then
+  begin
+    System.SetLength(outBytes, outLength);
+  end
+  else
+  begin
+    outBytes := Nil;
+  end;
+
+  &pos := ProcessBytes(input, inOff, length, outBytes, 0);
+
+  if ((outLength > 0) and (pos < outLength)) then
+  begin
+    System.SetLength(tmp, &pos);
+    System.Move(outBytes[0], tmp[0], &pos * System.SizeOf(Byte));
+
+    outBytes := tmp;
+  end;
+
+  result := outBytes;
+
+end;
+
+procedure TBufferedBlockCipher.Reset;
+begin
+  TArrayUtils.Fill(Fbuf, 0, System.length(Fbuf), Byte(0));
+  FbufOff := 0;
+
+  Fcipher.Reset();
+end;
+
+end.

+ 372 - 0
src/libraries/cryptolib4pascal/ClpBufferedCipherBase.pas

@@ -0,0 +1,372 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpBufferedCipherBase;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpIBufferedCipher,
+  ClpICipherParameters,
+  ClpIBufferedCipherBase,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SOutputBufferTooSmall = 'Output Buffer too Short';
+  SInvalidBufferSize = '"BufferSize" Must Be Greater Than Zero';
+  SInputOutputStreamSame =
+    'Input and Output Streams Must not Point to the Same Stream Instance';
+  SUnAssignedInputStream = 'Input Stream Is Unassigned';
+  SUnAssignedOutputStream = 'Output Stream Is Unassigned';
+  SPositionOutOfRange = 'Current Position Is Out Of Range';
+  SStreamPositionOutOfRange =
+    'Stream Position (or Stream Length to Process) Is Out Of Range';
+
+type
+  TBufferedCipherBase = class abstract(TInterfacedObject, IBufferedCipherBase,
+    IBufferedCipher)
+
+  strict private
+
+    class function GetEmptyBuffer: TCryptoLibByteArray; static; inline;
+
+  var
+    FBufferSize: Int32;
+    FOnProgress: TBufferedCipherProgressEvent;
+
+  const
+    BUFFER_SIZE = Int32(64 * 1024); // 64Kb
+
+  strict protected
+
+    function GetBufferSize: Int32; inline;
+    procedure SetBufferSize(value: Int32); inline;
+    function GetOnProgress: TBufferedCipherProgressEvent; inline;
+    procedure SetOnProgress(const value: TBufferedCipherProgressEvent); inline;
+    procedure DoProgress(AProcessed, ATotal: Int64); virtual;
+
+    class property EmptyBuffer: TCryptoLibByteArray read GetEmptyBuffer;
+
+  public
+
+    constructor Create();
+
+    procedure Init(forEncryption: Boolean; const parameters: ICipherParameters);
+      virtual; abstract;
+
+    function GetBlockSize(): Int32; virtual; abstract;
+
+    function GetOutputSize(inputLen: Int32): Int32; virtual; abstract;
+    function GetUpdateOutputSize(inputLen: Int32): Int32; virtual; abstract;
+
+    function ProcessByte(input: Byte): TCryptoLibByteArray; overload;
+      virtual; abstract;
+
+    function ProcessByte(input: Byte; const output: TCryptoLibByteArray;
+      outOff: Int32): Int32; overload; virtual;
+
+    function ProcessBytes(const input: TCryptoLibByteArray)
+      : TCryptoLibByteArray; overload; virtual;
+
+    function ProcessBytes(const input: TCryptoLibByteArray;
+      inOff, length: Int32): TCryptoLibByteArray; overload; virtual; abstract;
+
+    function ProcessBytes(const input, output: TCryptoLibByteArray;
+      outOff: Int32): Int32; overload; virtual;
+
+    function ProcessBytes(const input: TCryptoLibByteArray; inOff: Int32;
+      length: Int32; const output: TCryptoLibByteArray; outOff: Int32): Int32;
+      overload; virtual;
+
+    procedure ProcessStream(const inputStream, outputStream: TStream;
+      length: Int64); overload; virtual;
+
+    procedure ProcessStream(const inputStream: TStream; inPos: Int64;
+      const outputStream: TStream; outPos: Int64; length: Int64);
+      overload; virtual;
+
+    function DoFinal(): TCryptoLibByteArray; overload; virtual; abstract;
+
+    function DoFinal(const input: TCryptoLibByteArray): TCryptoLibByteArray;
+      overload; virtual;
+
+    function DoFinal(const input: TCryptoLibByteArray; inOff, length: Int32)
+      : TCryptoLibByteArray; overload; virtual; abstract;
+
+    function DoFinal(const output: TCryptoLibByteArray; outOff: Int32): Int32;
+      overload; virtual;
+
+    function DoFinal(const input, output: TCryptoLibByteArray; outOff: Int32)
+      : Int32; overload; virtual;
+
+    function DoFinal(const input: TCryptoLibByteArray; inOff, length: Int32;
+      const output: TCryptoLibByteArray; outOff: Int32): Int32;
+      overload; virtual;
+
+    procedure Reset(); virtual; abstract;
+
+    function GetAlgorithmName: String; virtual; abstract;
+    property AlgorithmName: String read GetAlgorithmName;
+
+    /// <summary>
+    /// property for determining the buffer size to use for stream based
+    /// encryption/decryption.
+    /// </summary>
+    property BufferSize: Int32 read GetBufferSize write SetBufferSize;
+    property OnProgress: TBufferedCipherProgressEvent read GetOnProgress
+      write SetOnProgress;
+
+  end;
+
+implementation
+
+{ TBufferedCipherBase }
+
+procedure TBufferedCipherBase.DoProgress(AProcessed, ATotal: Int64);
+begin
+  if System.Assigned(FOnProgress) then
+  begin
+    FOnProgress(AProcessed, ATotal);
+  end;
+end;
+
+function TBufferedCipherBase.GetOnProgress: TBufferedCipherProgressEvent;
+begin
+  result := FOnProgress;
+end;
+
+procedure TBufferedCipherBase.SetOnProgress(const value
+  : TBufferedCipherProgressEvent);
+begin
+  FOnProgress := value;
+end;
+
+constructor TBufferedCipherBase.Create;
+begin
+  Inherited Create();
+  FBufferSize := BUFFER_SIZE;
+end;
+
+function TBufferedCipherBase.GetBufferSize: Int32;
+begin
+  result := FBufferSize;
+end;
+
+procedure TBufferedCipherBase.SetBufferSize(value: Int32);
+begin
+  if value > 0 then
+  begin
+    FBufferSize := value;
+  end
+  else
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidBufferSize);
+  end;
+end;
+
+function TBufferedCipherBase.DoFinal(const output: TCryptoLibByteArray;
+  outOff: Int32): Int32;
+var
+  outBytes: TCryptoLibByteArray;
+begin
+  outBytes := DoFinal();
+  if ((outOff + System.length(outBytes)) > System.length(output)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooSmall);
+  end;
+  System.Move(outBytes[0], output[outOff], System.length(outBytes));
+  result := System.length(outBytes);
+end;
+
+function TBufferedCipherBase.DoFinal(const input: TCryptoLibByteArray)
+  : TCryptoLibByteArray;
+begin
+  result := DoFinal(input, 0, System.length(input));
+end;
+
+function TBufferedCipherBase.DoFinal(const input: TCryptoLibByteArray;
+  inOff, length: Int32; const output: TCryptoLibByteArray;
+  outOff: Int32): Int32;
+var
+  len: Int32;
+begin
+  len := ProcessBytes(input, inOff, length, output, outOff);
+  len := len + DoFinal(output, outOff + len);
+  result := len;
+end;
+
+function TBufferedCipherBase.DoFinal(const input, output: TCryptoLibByteArray;
+  outOff: Int32): Int32;
+begin
+  result := DoFinal(input, 0, System.length(input), output, outOff);
+end;
+
+class function TBufferedCipherBase.GetEmptyBuffer: TCryptoLibByteArray;
+begin
+  result := Nil;
+end;
+
+function TBufferedCipherBase.ProcessByte(input: Byte;
+  const output: TCryptoLibByteArray; outOff: Int32): Int32;
+var
+  outBytes: TCryptoLibByteArray;
+begin
+  outBytes := ProcessByte(input);
+  if (outBytes = Nil) then
+  begin
+    result := 0;
+    Exit;
+  end;
+  if ((outOff + System.length(outBytes)) > System.length(output)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooSmall);
+  end;
+  System.Move(outBytes[0], output[outOff], System.length(outBytes));
+  result := System.length(outBytes);
+end;
+
+function TBufferedCipherBase.ProcessBytes(const input: TCryptoLibByteArray)
+  : TCryptoLibByteArray;
+begin
+  result := ProcessBytes(input, 0, System.length(input));
+end;
+
+function TBufferedCipherBase.ProcessBytes(const input: TCryptoLibByteArray;
+  inOff, length: Int32; const output: TCryptoLibByteArray;
+  outOff: Int32): Int32;
+var
+  outBytes: TCryptoLibByteArray;
+begin
+  outBytes := ProcessBytes(input, inOff, length);
+  if (outBytes = Nil) then
+  begin
+    result := 0;
+    Exit;
+  end;
+  if ((outOff + System.length(outBytes)) > System.length(output)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooSmall);
+  end;
+  System.Move(outBytes[0], output[outOff], System.length(outBytes));
+  result := System.length(outBytes);
+end;
+
+procedure TBufferedCipherBase.ProcessStream(const inputStream: TStream;
+  inPos: Int64; const outputStream: TStream; outPos, length: Int64);
+var
+  LBufferSize, readed: Int32;
+  total: Int64;
+  data, tempRes: TCryptoLibByteArray;
+begin
+  total := 0;
+  if ((inPos < 0) or (outPos < 0) or (length <= 0)) then
+  begin
+    raise EIndexOutOfRangeCryptoLibException.CreateRes
+      (@SStreamPositionOutOfRange);
+  end;
+
+  if inputStream = Nil then
+  begin
+    raise EStreamCryptoLibException.CreateRes(@SUnAssignedInputStream);
+  end;
+
+  if outputStream = Nil then
+  begin
+    raise EStreamCryptoLibException.CreateRes(@SUnAssignedOutputStream);
+  end;
+
+  if inputStream = outputStream then
+  begin
+    raise EStreamCryptoLibException.CreateRes(@SInputOutputStreamSame);
+  end;
+
+  if ((inputStream.Position + length) > inputStream.Size) then
+  begin
+    raise EIndexOutOfRangeCryptoLibException.CreateRes(@SPositionOutOfRange);
+  end;
+
+  if (inputStream.Position >= inputStream.Size) then
+  begin
+    Exit;
+  end;
+
+  if BufferSize > inputStream.Size then // Sanity Check
+  begin
+    LBufferSize := BUFFER_SIZE;
+  end
+  else
+  begin
+    LBufferSize := BufferSize;
+  end;
+
+  System.SetLength(data, LBufferSize);
+
+  inputStream.Position := inPos;
+  outputStream.Position := outPos;
+
+  DoProgress(0, length);
+
+  while true do
+  begin
+    readed := inputStream.Read(data[0], LBufferSize);
+
+    if ((total + Int64(readed)) >= length) then
+    begin
+      tempRes := ProcessBytes(data, 0, Int32(length - total));
+      DoProgress(total + readed, length);
+      if (tempRes <> Nil) then
+      begin
+        outputStream.Write(tempRes[0], System.length(tempRes));
+      end;
+      break;
+    end
+    else
+    begin
+      tempRes := ProcessBytes(data, 0, readed);
+      if (tempRes <> Nil) then
+      begin
+        outputStream.Write(tempRes[0], System.length(tempRes));
+      end;
+      total := total + readed;
+    end;
+    DoProgress(total, length);
+  end;
+
+  tempRes := DoFinal();
+  if (tempRes <> Nil) then
+  begin
+    outputStream.Write(tempRes[0], System.length(tempRes));
+  end;
+
+end;
+
+procedure TBufferedCipherBase.ProcessStream(const inputStream,
+  outputStream: TStream; length: Int64);
+begin
+  ProcessStream(inputStream, 0, outputStream, 0, length);
+end;
+
+function TBufferedCipherBase.ProcessBytes(const input,
+  output: TCryptoLibByteArray; outOff: Int32): Int32;
+begin
+  result := ProcessBytes(input, 0, System.length(input), output, outOff);
+end;
+
+end.

+ 69 - 0
src/libraries/cryptolib4pascal/ClpCheck.pas

@@ -0,0 +1,69 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpCheck;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpCryptoLibTypes;
+
+type
+  TCheck = class sealed(TObject)
+
+  public
+    class procedure DataLength(condition: Boolean; const msg: String); overload;
+    class procedure DataLength(const buf: TCryptoLibByteArray; off, len: Int32;
+      const msg: String); overload;
+    class procedure OutputLength(const buf: TCryptoLibByteArray;
+      off, len: Int32; const msg: String); overload;
+
+  end;
+
+implementation
+
+{ TCheck }
+
+class procedure TCheck.DataLength(condition: Boolean; const msg: String);
+begin
+  if condition then
+  begin
+    raise EDataLengthCryptoLibException.Create(msg);
+  end;
+end;
+
+class procedure TCheck.DataLength(const buf: TCryptoLibByteArray;
+  off, len: Int32; const msg: String);
+begin
+  if ((off + len) > System.Length(buf)) then
+  begin
+    raise EDataLengthCryptoLibException.Create(msg);
+  end;
+end;
+
+class procedure TCheck.OutputLength(const buf: TCryptoLibByteArray;
+  off, len: Int32; const msg: String);
+begin
+  if ((off + len) > System.Length(buf)) then
+  begin
+    raise EOutputLengthCryptoLibException.Create(msg);
+  end;
+end;
+
+end.

+ 154 - 0
src/libraries/cryptolib4pascal/ClpCipherKeyGenerator.pas

@@ -0,0 +1,154 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpCipherKeyGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpSecureRandom,
+  ClpISecureRandom,
+  ClpICipherKeyGenerator,
+  ClpKeyGenerationParameters,
+  ClpIKeyGenerationParameters,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SInvalidStrengthValue =
+    'Strength must be a Positive Value, "defaultStrength"';
+  SParametersNil = 'Parameters Cannot be Nil';
+  SGeneratorNotInitialized = 'Generator has not been Initialised';
+
+type
+
+  /// <summary>
+  /// The base class for symmetric, or secret, cipher key generators.
+  /// </summary>
+  TCipherKeyGenerator = class(TInterfacedObject, ICipherKeyGenerator)
+
+  strict private
+  var
+    Funinitialised: Boolean;
+    FdefaultStrength: Int32;
+
+    function GetdefaultStrength: Int32; inline;
+
+  strict protected
+  var
+    Frandom: ISecureRandom;
+    Fstrength: Int32;
+
+    procedure EngineInit(const parameters: IKeyGenerationParameters); virtual;
+    function EngineGenerateKey: TCryptoLibByteArray; virtual;
+
+  public
+
+    constructor Create(); overload;
+    constructor Create(defaultStrength: Int32); overload;
+
+    /// <summary>
+    /// initialise the key generator.
+    /// </summary>
+    /// <param name="parameters">
+    /// the parameters to be used for key generation
+    /// </param>
+    procedure Init(const parameters: IKeyGenerationParameters);
+
+    /// <summary>
+    /// Generate a secret key.
+    /// </summary>
+    /// <returns>
+    /// a byte array containing the key value.
+    /// </returns>
+    function GenerateKey: TCryptoLibByteArray;
+
+    property defaultStrength: Int32 read GetdefaultStrength;
+  end;
+
+implementation
+
+{ TCipherKeyGenerator }
+
+constructor TCipherKeyGenerator.Create;
+begin
+  Inherited Create();
+  Funinitialised := true;
+end;
+
+constructor TCipherKeyGenerator.Create(defaultStrength: Int32);
+begin
+  Inherited Create();
+  Funinitialised := true;
+  if (defaultStrength < 1) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidStrengthValue);
+  end;
+
+  FdefaultStrength := defaultStrength;
+end;
+
+function TCipherKeyGenerator.EngineGenerateKey: TCryptoLibByteArray;
+begin
+  result := TSecureRandom.GetNextBytes(Frandom, Fstrength);
+end;
+
+procedure TCipherKeyGenerator.EngineInit(const parameters
+  : IKeyGenerationParameters);
+begin
+  Frandom := parameters.Random;
+  Fstrength := (parameters.Strength + 7) div 8;
+end;
+
+function TCipherKeyGenerator.GenerateKey: TCryptoLibByteArray;
+begin
+  if (Funinitialised) then
+  begin
+    if (FdefaultStrength < 1) then
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes
+        (@SGeneratorNotInitialized);
+    end;
+
+    Funinitialised := false;
+
+    EngineInit(TKeyGenerationParameters.Create(TSecureRandom.Create()
+      as ISecureRandom, FdefaultStrength) as IKeyGenerationParameters);
+  end;
+
+  result := EngineGenerateKey();
+end;
+
+function TCipherKeyGenerator.GetdefaultStrength: Int32;
+begin
+  result := FdefaultStrength;
+end;
+
+procedure TCipherKeyGenerator.Init(const parameters: IKeyGenerationParameters);
+begin
+  if (parameters = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SParametersNil);
+  end;
+
+  Funinitialised := false;
+
+  EngineInit(parameters);
+end;
+
+end.

+ 458 - 0
src/libraries/cryptolib4pascal/ClpCipherUtilities.pas

@@ -0,0 +1,458 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpCipherUtilities;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  TypInfo,
+  Generics.Collections,
+  ClpCryptoLibTypes,
+  ClpStringUtils,
+  ClpPaddingModes,
+  ClpIPaddingModes,
+  ClpBlockCipherModes,
+  ClpIBlockCipherModes,
+  ClpBufferedBlockCipher,
+  ClpIBufferedBlockCipher,
+  ClpPaddedBufferedBlockCipher,
+  ClpIPaddedBufferedBlockCipher,
+  ClpNistObjectIdentifiers,
+  ClpIAsn1Objects,
+  ClpIBufferedCipher,
+  ClpIBlockCipher,
+  ClpAesEngine,
+  ClpIAesEngine,
+  ClpBlowfishEngine,
+  ClpIBlowfishEngine,
+  ClpIBlockCipherPadding;
+
+resourcestring
+  SMechanismNil = 'Mechanism Cannot be Nil';
+  SAlgorithmNil = 'Algorithm Cannot be Nil';
+  SUnRecognizedCipher = '"Cipher " %s Not Recognised.';
+  SSICModeWarning =
+    'Warning: SIC-Mode Can Become a TwoTime-Pad if the Blocksize of the Cipher is Too Small. Use a Cipher With a Block Size of at Least 128 bits (e.g. AES)';
+
+type
+
+  /// <remarks>
+  /// Cipher Utility class contains methods that can not be specifically grouped into other classes.
+  /// </remarks>
+  TCipherUtilities = class sealed(TObject)
+
+  strict private
+
+  type
+{$SCOPEDENUMS ON}
+    TCipherAlgorithm = (AES, BLOWFISH);
+    TCipherMode = (NONE, CBC, CFB, CTR, ECB, OFB, SIC);
+    TCipherPadding = (NOPADDING, ISO10126PADDING, ISO10126D2PADDING,
+      ISO10126_2PADDING, ISO7816_4PADDING, ISO9797_1PADDING, PKCS5,
+      PKCS5PADDING, PKCS7, PKCS7PADDING, TBCPADDING, X923PADDING,
+      ZEROBYTEPADDING);
+{$SCOPEDENUMS OFF}
+
+  class var
+
+    Falgorithms: TDictionary<String, String>;
+    Foids: TDictionary<String, IDerObjectIdentifier>;
+
+    class function GetAlgorithms: TCryptoLibStringArray; static; inline;
+    class function GetDigitIndex(const s: String): Int32; static; inline;
+
+    class procedure Boot(); static;
+    class constructor CreateCipherUtilities();
+    class destructor DestroyCipherUtilities();
+
+  public
+    /// <summary>
+    /// Returns a ObjectIdentifier for a give encoding.
+    /// </summary>
+    /// <param name="mechanism">A string representation of the encoding.</param>
+    /// <returns>A DerObjectIdentifier, null if the Oid is not available.</returns>
+    // TODO Don't really want to support this
+    class function GetObjectIdentifier(mechanism: String)
+      : IDerObjectIdentifier; static;
+    class function GetCipher(algorithm: String): IBufferedCipher;
+      overload; static;
+    class function GetCipher(const oid: IDerObjectIdentifier): IBufferedCipher;
+      overload; static; inline;
+
+    class property Algorithms: TCryptoLibStringArray read GetAlgorithms;
+  end;
+
+implementation
+
+{ TCipherUtilities }
+
+class procedure TCipherUtilities.Boot;
+begin
+  Falgorithms := TDictionary<string, string>.Create();
+  Foids := TDictionary<string, IDerObjectIdentifier>.Create();
+
+  TNistObjectIdentifiers.Boot;
+
+  // TODO Flesh out the list of aliases
+
+  Falgorithms.Add(TNistObjectIdentifiers.IdAes128Ecb.Id,
+    'AES/ECB/PKCS7PADDING');
+  Falgorithms.Add(TNistObjectIdentifiers.IdAes192Ecb.Id,
+    'AES/ECB/PKCS7PADDING');
+  Falgorithms.Add(TNistObjectIdentifiers.IdAes256Ecb.Id,
+    'AES/ECB/PKCS7PADDING');
+  Falgorithms.Add('AES//PKCS7', 'AES/ECB/PKCS7PADDING');
+  Falgorithms.Add('AES//PKCS7PADDING', 'AES/ECB/PKCS7PADDING');
+  Falgorithms.Add('AES//PKCS5', 'AES/ECB/PKCS7PADDING');
+  Falgorithms.Add('AES//PKCS5PADDING', 'AES/ECB/PKCS7PADDING');
+
+  Falgorithms.Add(TNistObjectIdentifiers.IdAes128Cbc.Id,
+    'AES/CBC/PKCS7PADDING');
+  Falgorithms.Add(TNistObjectIdentifiers.IdAes192Cbc.Id,
+    'AES/CBC/PKCS7PADDING');
+  Falgorithms.Add(TNistObjectIdentifiers.IdAes256Cbc.Id,
+    'AES/CBC/PKCS7PADDING');
+
+  Falgorithms.Add(TNistObjectIdentifiers.IdAes128Ofb.Id, 'AES/OFB/NOPADDING');
+  Falgorithms.Add(TNistObjectIdentifiers.IdAes192Ofb.Id, 'AES/OFB/NOPADDING');
+  Falgorithms.Add(TNistObjectIdentifiers.IdAes256Ofb.Id, 'AES/OFB/NOPADDING');
+
+  Falgorithms.Add(TNistObjectIdentifiers.IdAes128Cfb.Id, 'AES/CFB/NOPADDING');
+  Falgorithms.Add(TNistObjectIdentifiers.IdAes192Cfb.Id, 'AES/CFB/NOPADDING');
+  Falgorithms.Add(TNistObjectIdentifiers.IdAes256Cfb.Id, 'AES/CFB/NOPADDING');
+
+  Falgorithms.Add('1.3.6.1.4.1.3029.1.2', 'BLOWFISH/CBC');
+
+end;
+
+class constructor TCipherUtilities.CreateCipherUtilities;
+begin
+  TCipherUtilities.Boot;
+end;
+
+class destructor TCipherUtilities.DestroyCipherUtilities;
+begin
+  Falgorithms.Free;
+  Foids.Free;
+end;
+
+class function TCipherUtilities.GetAlgorithms: TCryptoLibStringArray;
+begin
+  Result := Foids.Keys.ToArray;
+end;
+
+class function TCipherUtilities.GetDigitIndex(const s: String): Int32;
+var
+  i, LowPoint, HighPoint: Int32;
+begin
+{$IFDEF DELPHIXE3_UP}
+  LowPoint := System.Low(s);
+  HighPoint := System.High(s);
+{$ELSE}
+  LowPoint := 1;
+  HighPoint := System.Length(s);
+{$ENDIF DELPHIXE3_UP}
+  For i := LowPoint to HighPoint do
+  begin
+    if (CharInSet(s[i], ['0' .. '9'])) then
+    begin
+      Result := i;
+      Exit;
+    end;
+  end;
+  Result := -1;
+end;
+
+class function TCipherUtilities.GetCipher(algorithm: String): IBufferedCipher;
+var
+  aliased, algorithmName, temp, paddingName, mode, modeName: string;
+  di, LowPoint, bits, HighPoint: Int32;
+  padded: Boolean;
+  parts: TCryptoLibStringArray;
+  cipherAlgorithm: TCipherAlgorithm;
+  cipherPadding: TCipherPadding;
+  cipherMode: TCipherMode;
+  blockCipher: IBlockCipher;
+  padding: IBlockCipherPadding;
+begin
+  if (algorithm = '') then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SAlgorithmNil);
+  end;
+  algorithm := UpperCase(algorithm);
+
+  if (Falgorithms.TryGetValue(algorithm, aliased)) then
+  begin
+    algorithm := aliased;
+  end;
+
+  parts := TStringUtils.SplitString(algorithm, '/');
+
+  blockCipher := Nil;
+
+  algorithmName := parts[0];
+
+  if (Falgorithms.TryGetValue(algorithmName, aliased)) then
+  begin
+    algorithmName := aliased;
+  end;
+
+  temp := StringReplace(algorithmName, '-', '_', [rfReplaceAll, rfIgnoreCase]);
+
+  temp := StringReplace(temp, '/', '_', [rfReplaceAll, rfIgnoreCase]);
+
+  cipherAlgorithm := TCipherAlgorithm
+    (GetEnumValue(TypeInfo(TCipherAlgorithm), temp));
+
+  case cipherAlgorithm of
+    TCipherAlgorithm.AES:
+      begin
+        blockCipher := TAesEngine.Create() as IAesEngine;
+      end;
+    TCipherAlgorithm.BLOWFISH:
+      begin
+        blockCipher := TBlowfishEngine.Create() as IBlowfishEngine;
+      end
+  else
+    begin
+      raise ESecurityUtilityCryptoLibException.CreateResFmt
+        (@SUnRecognizedCipher, [algorithm]);
+    end;
+  end;
+
+  padded := true;
+  padding := Nil;
+
+  if System.Length(parts) > 2 then
+  begin
+    paddingName := parts[2];
+
+    temp := StringReplace(paddingName, '-', '_', [rfReplaceAll, rfIgnoreCase]);
+
+    temp := StringReplace(temp, '/', '_', [rfReplaceAll, rfIgnoreCase]);
+
+    cipherPadding := TCipherPadding
+      (GetEnumValue(TypeInfo(TCipherPadding), temp));
+
+    case cipherPadding of
+      TCipherPadding.NOPADDING:
+        begin
+          padded := false;
+        end;
+
+      TCipherPadding.ISO10126PADDING, TCipherPadding.ISO10126D2PADDING,
+        TCipherPadding.ISO10126_2PADDING:
+        begin
+          padding := TISO10126d2Padding.Create() as IISO10126d2Padding;
+        end;
+
+      TCipherPadding.ISO7816_4PADDING, TCipherPadding.ISO9797_1PADDING:
+        begin
+          padding := TISO7816d4Padding.Create() as IISO7816d4Padding;
+        end;
+
+      TCipherPadding.PKCS5, TCipherPadding.PKCS5PADDING, TCipherPadding.PKCS7,
+        TCipherPadding.PKCS7PADDING:
+        begin
+          padding := TPkcs7Padding.Create() as IPkcs7Padding;
+        end;
+
+      TCipherPadding.TBCPADDING:
+        begin
+          padding := TTBCPadding.Create() as ITBCPadding;
+        end;
+
+      TCipherPadding.X923PADDING:
+        begin
+          padding := TX923Padding.Create() as IX923Padding;
+        end;
+
+      TCipherPadding.ZEROBYTEPADDING:
+        begin
+          padding := TZeroBytePadding.Create() as IZeroBytePadding;
+        end
+
+    else
+      begin
+        raise ESecurityUtilityCryptoLibException.CreateResFmt
+          (@SUnRecognizedCipher, [algorithm]);
+      end;
+    end;
+
+  end;
+
+  mode := '';
+  if (System.Length(parts) > 1) then
+  begin
+    mode := parts[1];
+
+    di := GetDigitIndex(mode);
+    if di >= 0 then
+    begin
+{$IFDEF DELPHIXE3_UP}
+      LowPoint := System.Low(mode);
+{$ELSE}
+      LowPoint := 1;
+{$ENDIF DELPHIXE3_UP}
+      modeName := System.Copy(mode, LowPoint, di);
+    end
+    else
+    begin
+      modeName := mode;
+    end;
+
+    if modeName = '' then
+    begin
+      cipherMode := TCipherMode.NONE;
+    end
+    else
+    begin
+      temp := StringReplace(modeName, '-', '_', [rfReplaceAll, rfIgnoreCase]);
+
+      temp := StringReplace(temp, '/', '_', [rfReplaceAll, rfIgnoreCase]);
+
+      cipherMode := TCipherMode(GetEnumValue(TypeInfo(TCipherMode), temp));
+    end;
+
+    case cipherMode of
+      TCipherMode.ECB, TCipherMode.NONE:
+        begin
+          // do nothing
+        end;
+
+      TCipherMode.CBC:
+        begin
+          blockCipher := TCbcBlockCipher.Create(blockCipher) as ICbcBlockCipher;
+        end;
+
+      TCipherMode.CFB:
+        begin
+          if (di < 0) then
+          begin
+            bits := 8 * blockCipher.GetBlockSize();
+          end
+          else
+          begin
+{$IFDEF DELPHIXE3_UP}
+            HighPoint := System.High(mode);
+{$ELSE}
+            HighPoint := System.Length(mode);
+{$ENDIF DELPHIXE3_UP}
+            bits := StrToInt(System.Copy(mode, di, HighPoint - di));
+          end;
+
+          blockCipher := TCfbBlockCipher.Create(blockCipher, bits)
+            as ICfbBlockCipher;
+        end;
+
+      TCipherMode.CTR:
+        begin
+          blockCipher := TSicBlockCipher.Create(blockCipher) as ISicBlockCipher;
+        end;
+
+      TCipherMode.OFB:
+        begin
+          if (di < 0) then
+          begin
+            bits := 8 * blockCipher.GetBlockSize();
+          end
+          else
+          begin
+{$IFDEF DELPHIXE3_UP}
+            HighPoint := System.High(mode);
+{$ELSE}
+            HighPoint := System.Length(mode);
+{$ENDIF DELPHIXE3_UP}
+            bits := StrToInt(System.Copy(mode, di, HighPoint - di));
+          end;
+
+          blockCipher := TOfbBlockCipher.Create(blockCipher, bits)
+            as IOfbBlockCipher;
+        end;
+
+      TCipherMode.SIC:
+        begin
+          if (blockCipher.GetBlockSize() < 16) then
+          begin
+            raise EArgumentCryptoLibException.CreateRes(@SSICModeWarning);
+          end;
+          blockCipher := TSicBlockCipher.Create(blockCipher) as ISicBlockCipher;
+        end
+
+    else
+      begin
+        raise ESecurityUtilityCryptoLibException.CreateResFmt
+          (@SUnRecognizedCipher, [algorithm]);
+      end;
+    end;
+  end;
+
+  if (blockCipher <> Nil) then
+  begin
+
+    if (padding <> Nil) then
+    begin
+      Result := TPaddedBufferedBlockCipher.Create(blockCipher, padding)
+        as IPaddedBufferedBlockCipher;
+      Exit;
+    end;
+
+    if ((not padded) or (blockCipher.IsPartialBlockOkay)) then
+    begin
+      Result := TBufferedBlockCipher.Create(blockCipher)
+        as IBufferedBlockCipher;
+      Exit;
+    end;
+
+    Result := TPaddedBufferedBlockCipher.Create(blockCipher)
+      as IPaddedBufferedBlockCipher;
+    Exit;
+  end;
+
+  raise ESecurityUtilityCryptoLibException.CreateResFmt(@SUnRecognizedCipher,
+    [algorithm]);
+end;
+
+class function TCipherUtilities.GetCipher(const oid: IDerObjectIdentifier)
+  : IBufferedCipher;
+begin
+  Result := GetCipher(oid.Id);
+end;
+
+class function TCipherUtilities.GetObjectIdentifier(mechanism: String)
+  : IDerObjectIdentifier;
+var
+  aliased: String;
+begin
+  if (mechanism = '') then
+    raise EArgumentNilCryptoLibException.CreateRes(@SMechanismNil);
+
+  mechanism := UpperCase(mechanism);
+  if Falgorithms.TryGetValue(mechanism, aliased) then
+  begin
+    mechanism := aliased;
+  end;
+
+  Foids.TryGetValue(mechanism, Result);
+
+end;
+
+end.

+ 546 - 0
src/libraries/cryptolib4pascal/ClpConverters.pas

@@ -0,0 +1,546 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpConverters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  StrUtils,
+  SysUtils,
+  ClpCryptoLibTypes,
+  ClpBits,
+  ClpBitConverter;
+
+resourcestring
+  SEncodingInstanceNil = 'Encoding Instance Cannot Be Nil';
+
+type
+  TConverters = class sealed(TObject)
+
+  strict private
+    class function SplitString(const S: String; Delimiter: Char)
+      : TCryptoLibStringArray; static;
+
+{$IFDEF DEBUG}
+    class procedure Check(const a_in: TCryptoLibByteArray;
+      a_in_size, a_out_size: Int32); overload; static;
+{$ENDIF DEBUG}
+    class procedure swap_copy_str_to_u32(src: Pointer; src_index: Int32;
+      dest: Pointer; dest_index: Int32; length: Int32); static;
+
+    class procedure swap_copy_str_to_u64(src: Pointer; src_index: Int32;
+      dest: Pointer; dest_index: Int32; length: Int32); static;
+
+  public
+
+    class function be2me_32(x: UInt32): UInt32; static; inline;
+
+    class function be2me_64(x: UInt64): UInt64; static; inline;
+
+    class function le2me_32(x: UInt32): UInt32; static; inline;
+
+    class function le2me_64(x: UInt64): UInt64; static; inline;
+
+    class procedure be32_copy(src: Pointer; src_index: Int32; dest: Pointer;
+      dest_index: Int32; length: Int32); static; inline;
+
+    class procedure le32_copy(src: Pointer; src_index: Int32; dest: Pointer;
+      dest_index: Int32; length: Int32); static; inline;
+
+    class procedure be64_copy(src: Pointer; src_index: Int32; dest: Pointer;
+      dest_index: Int32; length: Int32); static; inline;
+
+    class procedure le64_copy(src: Pointer; src_index: Int32; dest: Pointer;
+      dest_index: Int32; length: Int32); static; inline;
+
+    class function ReadBytesAsUInt32LE(a_in: PByte; a_index: Int32): UInt32;
+      static; inline;
+
+    class function ReadBytesAsUInt32BE(a_in: PByte; a_index: Int32): UInt32;
+      static; inline;
+
+    class function ReadBytesAsUInt64LE(a_in: PByte; a_index: Int32): UInt64;
+      static; inline;
+
+    class function ReadBytesAsUInt64BE(a_in: PByte; a_index: Int32): UInt64;
+      static; inline;
+
+    class function ReadUInt32AsBytesLE(a_in: UInt32): TCryptoLibByteArray;
+      overload; static; inline;
+
+    class function ReadUInt32AsBytesBE(a_in: UInt32): TCryptoLibByteArray;
+      overload; static; inline;
+
+    class function ReadUInt64AsBytesBE(a_in: UInt64): TCryptoLibByteArray;
+      overload; static; inline;
+
+    class function ReadUInt64AsBytesLE(a_in: UInt64): TCryptoLibByteArray;
+      overload; static; inline;
+
+    class procedure ReadUInt32AsBytesLE(a_in: UInt32;
+      const a_out: TCryptoLibByteArray; a_index: Int32); overload;
+      static; inline;
+
+    class procedure ReadUInt32AsBytesBE(a_in: UInt32;
+      const a_out: TCryptoLibByteArray; a_index: Int32); overload;
+      static; inline;
+
+    class procedure ReadUInt64AsBytesLE(a_in: UInt64;
+      const a_out: TCryptoLibByteArray; a_index: Int32); overload;
+      static; inline;
+
+    class procedure ReadUInt64AsBytesBE(a_in: UInt64;
+      const a_out: TCryptoLibByteArray; a_index: Int32); overload;
+      static; inline;
+
+    class function ConvertStringToBytes(const a_in: String;
+      a_encoding: TEncoding): TCryptoLibByteArray; overload; static;
+
+    class function ConvertBytesToString(const a_in: TCryptoLibByteArray;
+      const a_encoding: TEncoding): String; overload; static;
+
+    class function ConvertHexStringToBytes(const a_in: String)
+      : TCryptoLibByteArray; static; inline;
+
+    class function ConvertBytesToHexString(const a_in: TCryptoLibByteArray;
+      a_group: Boolean): String; static;
+
+  end;
+
+implementation
+
+{ TConverters }
+
+{$IFDEF DEBUG}
+
+class procedure TConverters.Check(const a_in: TCryptoLibByteArray;
+  a_in_size, a_out_size: Int32);
+begin
+  System.Assert(((System.length(a_in) * a_in_size) mod a_out_size) = 0);
+end;
+
+{$ENDIF DEBUG}
+
+class procedure TConverters.swap_copy_str_to_u32(src: Pointer; src_index: Int32;
+  dest: Pointer; dest_index: Int32; length: Int32);
+var
+  lsrc, ldest, lend: PCardinal;
+  lbsrc: PByte;
+  lLength: Int32;
+begin
+  // if all pointers and length are 32-bits aligned
+  if ((Int32(PByte(dest) - PByte(0)) or (PByte(src) - PByte(0)) or src_index or
+    dest_index or length) and 3) = 0 then
+  begin
+    // copy memory as 32-bit words
+    lsrc := PCardinal(PByte(src) + src_index);
+    lend := PCardinal((PByte(src) + src_index) + length);
+    ldest := PCardinal(PByte(dest) + dest_index);
+    while lsrc < lend do
+    begin
+      ldest^ := TBits.ReverseBytesUInt32(lsrc^);
+      System.Inc(ldest);
+      System.Inc(lsrc);
+    end;
+  end
+  else
+  begin
+    lbsrc := (PByte(src) + src_index);
+
+    lLength := length + dest_index;
+    while dest_index < lLength do
+    begin
+
+      PByte(dest)[dest_index xor 3] := lbsrc^;
+
+      System.Inc(lbsrc);
+      System.Inc(dest_index);
+    end;
+
+  end;
+end;
+
+class procedure TConverters.swap_copy_str_to_u64(src: Pointer; src_index: Int32;
+  dest: Pointer; dest_index: Int32; length: Int32);
+var
+  lsrc, ldest, lend: PUInt64;
+  lbsrc: PByte;
+  lLength: Int32;
+begin
+  // if all pointers and length are 64-bits aligned
+  if ((Int32(PByte(dest) - PByte(0)) or (PByte(src) - PByte(0)) or src_index or
+    dest_index or length) and 7) = 0 then
+  begin
+    // copy aligned memory block as 64-bit integers
+    lsrc := PUInt64(PByte(src) + src_index);
+    lend := PUInt64((PByte(src) + src_index) + length);
+    ldest := PUInt64(PByte(dest) + dest_index);
+    while lsrc < lend do
+    begin
+      ldest^ := TBits.ReverseBytesUInt64(lsrc^);
+      System.Inc(ldest);
+      System.Inc(lsrc);
+    end;
+  end
+  else
+  begin
+    lbsrc := (PByte(src) + src_index);
+
+    lLength := length + dest_index;
+    while dest_index < lLength do
+    begin
+
+      PByte(dest)[dest_index xor 7] := lbsrc^;
+
+      System.Inc(lbsrc);
+      System.Inc(dest_index);
+    end;
+
+  end;
+end;
+
+class function TConverters.be2me_32(x: UInt32): UInt32;
+begin
+  if TBitConverter.IsLittleEndian then
+    result := TBits.ReverseBytesUInt32(x)
+  else
+    result := x;
+end;
+
+class function TConverters.be2me_64(x: UInt64): UInt64;
+begin
+  if TBitConverter.IsLittleEndian then
+    result := TBits.ReverseBytesUInt64(x)
+  else
+    result := x;
+end;
+
+class procedure TConverters.be32_copy(src: Pointer; src_index: Int32;
+  dest: Pointer; dest_index: Int32; length: Int32);
+begin
+  if TBitConverter.IsLittleEndian then
+    swap_copy_str_to_u32(src, src_index, dest, dest_index, length)
+  else
+    System.Move(Pointer(PByte(src) + src_index)^,
+      Pointer(PByte(dest) + dest_index)^, length);
+end;
+
+class procedure TConverters.be64_copy(src: Pointer; src_index: Int32;
+  dest: Pointer; dest_index: Int32; length: Int32);
+begin
+  if TBitConverter.IsLittleEndian then
+    swap_copy_str_to_u64(src, src_index, dest, dest_index, length)
+  else
+    System.Move(Pointer(PByte(src) + src_index)^,
+      Pointer(PByte(dest) + dest_index)^, length);
+end;
+
+class function TConverters.le2me_32(x: UInt32): UInt32;
+begin
+  if not TBitConverter.IsLittleEndian then
+    result := TBits.ReverseBytesUInt32(x)
+  else
+    result := x;
+end;
+
+class function TConverters.le2me_64(x: UInt64): UInt64;
+begin
+  if not TBitConverter.IsLittleEndian then
+    result := TBits.ReverseBytesUInt64(x)
+  else
+    result := x;
+end;
+
+class procedure TConverters.le32_copy(src: Pointer; src_index: Int32;
+  dest: Pointer; dest_index: Int32; length: Int32);
+begin
+  if TBitConverter.IsLittleEndian then
+    System.Move(Pointer(PByte(src) + src_index)^,
+      Pointer(PByte(dest) + dest_index)^, length)
+  else
+    swap_copy_str_to_u32(src, src_index, dest, dest_index, length);
+end;
+
+class procedure TConverters.le64_copy(src: Pointer; src_index: Int32;
+  dest: Pointer; dest_index: Int32; length: Int32);
+begin
+  if TBitConverter.IsLittleEndian then
+    System.Move(Pointer(PByte(src) + src_index)^,
+      Pointer(PByte(dest) + dest_index)^, length)
+  else
+    swap_copy_str_to_u64(src, src_index, dest, dest_index, length);
+end;
+
+class function TConverters.ReadBytesAsUInt32LE(a_in: PByte;
+  a_index: Int32): UInt32;
+begin
+{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
+  System.Move(a_in[a_index], result, System.SizeOf(UInt32));
+{$ELSE}
+  result := PCardinal(a_in + a_index)^;
+{$ENDIF FPC_REQUIRES_PROPER_ALIGNMENT}
+  result := le2me_32(result);
+end;
+
+class function TConverters.ReadBytesAsUInt32BE(a_in: PByte;
+  a_index: Int32): UInt32;
+begin
+{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
+  System.Move(a_in[a_index], result, System.SizeOf(UInt32));
+{$ELSE}
+  result := PCardinal(a_in + a_index)^;
+{$ENDIF FPC_REQUIRES_PROPER_ALIGNMENT}
+  result := be2me_32(result);
+end;
+
+class function TConverters.ReadBytesAsUInt64LE(a_in: PByte;
+  a_index: Int32): UInt64;
+begin
+{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
+  System.Move(a_in[a_index], result, System.SizeOf(UInt64));
+{$ELSE}
+  result := PUInt64(a_in + a_index)^;
+{$ENDIF FPC_REQUIRES_PROPER_ALIGNMENT}
+  result := le2me_64(result);
+end;
+
+class function TConverters.ReadBytesAsUInt64BE(a_in: PByte;
+  a_index: Int32): UInt64;
+begin
+{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
+  System.Move(a_in[a_index], result, System.SizeOf(UInt64));
+{$ELSE}
+  result := PUInt64(a_in + a_index)^;
+{$ENDIF FPC_REQUIRES_PROPER_ALIGNMENT}
+  result := be2me_64(result);
+end;
+
+class function TConverters.ReadUInt32AsBytesLE(a_in: UInt32)
+  : TCryptoLibByteArray;
+begin
+  result := TCryptoLibByteArray.Create(Byte(a_in), Byte(a_in shr 8),
+    Byte(a_in shr 16), Byte(a_in shr 24));
+end;
+
+class function TConverters.ReadUInt32AsBytesBE(a_in: UInt32)
+  : TCryptoLibByteArray;
+begin
+  result := TCryptoLibByteArray.Create(Byte(a_in shr 24), Byte(a_in shr 16),
+    Byte(a_in shr 8), Byte(a_in));
+end;
+
+class function TConverters.ReadUInt64AsBytesLE(a_in: UInt64)
+  : TCryptoLibByteArray;
+begin
+  result := TCryptoLibByteArray.Create(Byte(a_in), Byte(a_in shr 8),
+    Byte(a_in shr 16), Byte(a_in shr 24), Byte(a_in shr 32), Byte(a_in shr 40),
+    Byte(a_in shr 48), Byte(a_in shr 56));
+end;
+
+class function TConverters.ReadUInt64AsBytesBE(a_in: UInt64)
+  : TCryptoLibByteArray;
+begin
+  result := TCryptoLibByteArray.Create(Byte(a_in shr 56), Byte(a_in shr 48),
+    Byte(a_in shr 40), Byte(a_in shr 32), Byte(a_in shr 24), Byte(a_in shr 16),
+    Byte(a_in shr 8), Byte(a_in));
+end;
+
+class procedure TConverters.ReadUInt32AsBytesBE(a_in: UInt32;
+  const a_out: TCryptoLibByteArray; a_index: Int32);
+begin
+  a_out[a_index] := Byte(a_in shr 24);
+  a_out[a_index + 1] := Byte(a_in shr 16);
+  a_out[a_index + 2] := Byte(a_in shr 8);
+  a_out[a_index + 3] := Byte(a_in);
+end;
+
+class procedure TConverters.ReadUInt32AsBytesLE(a_in: UInt32;
+  const a_out: TCryptoLibByteArray; a_index: Int32);
+begin
+  a_out[a_index] := Byte(a_in);
+  a_out[a_index + 1] := Byte(a_in shr 8);
+  a_out[a_index + 2] := Byte(a_in shr 16);
+  a_out[a_index + 3] := Byte(a_in shr 24);
+end;
+
+class procedure TConverters.ReadUInt64AsBytesLE(a_in: UInt64;
+  const a_out: TCryptoLibByteArray; a_index: Int32);
+begin
+  a_out[a_index] := Byte(a_in);
+  a_out[a_index + 1] := Byte(a_in shr 8);
+  a_out[a_index + 2] := Byte(a_in shr 16);
+  a_out[a_index + 3] := Byte(a_in shr 24);
+  a_out[a_index + 4] := Byte(a_in shr 32);
+  a_out[a_index + 5] := Byte(a_in shr 40);
+  a_out[a_index + 6] := Byte(a_in shr 48);
+  a_out[a_index + 7] := Byte(a_in shr 56);
+end;
+
+class procedure TConverters.ReadUInt64AsBytesBE(a_in: UInt64;
+  const a_out: TCryptoLibByteArray; a_index: Int32);
+begin
+  a_out[a_index] := Byte(a_in shr 56);
+  a_out[a_index + 1] := Byte(a_in shr 48);
+  a_out[a_index + 2] := Byte(a_in shr 40);
+  a_out[a_index + 3] := Byte(a_in shr 32);
+  a_out[a_index + 4] := Byte(a_in shr 24);
+  a_out[a_index + 5] := Byte(a_in shr 16);
+  a_out[a_index + 6] := Byte(a_in shr 8);
+  a_out[a_index + 7] := Byte(a_in);
+end;
+
+class function TConverters.ConvertBytesToHexString
+  (const a_in: TCryptoLibByteArray; a_group: Boolean): String;
+var
+  I: Int32;
+  hex, workstring: String;
+  ar: TCryptoLibStringArray;
+begin
+
+  hex := UpperCase(TBitConverter.ToString(a_in));
+
+  if System.length(a_in) = 1 then
+  begin
+    result := hex;
+    Exit;
+  end;
+
+  if System.length(a_in) = 2 then
+  begin
+    result := StringReplace(hex, '-', '', [rfIgnoreCase, rfReplaceAll]);
+    Exit;
+  end;
+
+  if (a_group) then
+  begin
+{$IFDEF DEBUG}
+    Check(a_in, 1, 4);
+{$ENDIF DEBUG}
+    workstring := UpperCase(TBitConverter.ToString(a_in));
+
+    ar := TConverters.SplitString(workstring, '-');
+    hex := '';
+    I := 0;
+
+    while I < (System.length(ar) shr 2) do
+    begin
+      if (I <> 0) then
+        hex := hex + '-';
+      hex := hex + ar[I * 4] + ar[I * 4 + 1] + ar[I * 4 + 2] + ar[I * 4 + 3];
+
+      System.Inc(I);
+    end;
+
+  end
+  else
+  begin
+    hex := StringReplace(hex, '-', '', [rfIgnoreCase, rfReplaceAll]);
+  end;
+  result := hex;
+end;
+
+class function TConverters.ConvertHexStringToBytes(const a_in: String)
+  : TCryptoLibByteArray;
+var
+  l_in: string;
+begin
+  l_in := a_in;
+  l_in := StringReplace(l_in, '-', '', [rfIgnoreCase, rfReplaceAll]);
+
+{$IFDEF DEBUG}
+  System.Assert(System.length(l_in) and 1 = 0);
+{$ENDIF DEBUG}
+  System.SetLength(result, System.length(l_in) shr 1);
+{$IFNDEF NEXTGEN}
+  HexToBin(PChar(l_in), @result[0], System.length(result));
+{$ELSE}
+  HexToBin(PChar(l_in), 0, result, 0, System.length(l_in));
+{$ENDIF !NEXTGEN}
+end;
+
+class function TConverters.ConvertStringToBytes(const a_in: String;
+  a_encoding: TEncoding): TCryptoLibByteArray;
+begin
+
+  if a_encoding = Nil then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SEncodingInstanceNil);
+  end;
+
+{$IFDEF FPC}
+  result := a_encoding.GetBytes(UnicodeString(a_in));
+{$ELSE}
+  result := a_encoding.GetBytes(a_in);
+{$ENDIF FPC}
+end;
+
+class function TConverters.ConvertBytesToString(const a_in: TCryptoLibByteArray;
+  const a_encoding: TEncoding): String;
+begin
+
+  if a_encoding = Nil then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SEncodingInstanceNil);
+  end;
+
+{$IFDEF FPC}
+  result := String(a_encoding.GetString(a_in));
+{$ELSE}
+  result := a_encoding.GetString(a_in);
+{$ENDIF FPC}
+end;
+
+class function TConverters.SplitString(const S: String; Delimiter: Char)
+  : TCryptoLibStringArray;
+var
+  PosStart, PosDel, SplitPoints, I, Len: Int32;
+begin
+  result := Nil;
+  if S <> '' then
+  begin
+    { Determine the length of the resulting array }
+    SplitPoints := 0;
+    for I := 1 to System.length(S) do
+    begin
+      if (Delimiter = S[I]) then
+        System.Inc(SplitPoints);
+    end;
+
+    System.SetLength(result, SplitPoints + 1);
+
+    { Split the string and fill the resulting array }
+
+    I := 0;
+    Len := System.length(Delimiter);
+    PosStart := 1;
+    PosDel := System.Pos(Delimiter, S);
+    while PosDel > 0 do
+    begin
+      result[I] := System.Copy(S, PosStart, PosDel - PosStart);
+      PosStart := PosDel + Len;
+      PosDel := PosEx(Delimiter, S, PosStart);
+      System.Inc(I);
+    end;
+    result[I] := System.Copy(S, PosStart, System.length(S));
+  end;
+end;
+
+end.

+ 168 - 0
src/libraries/cryptolib4pascal/ClpCryptoApiRandomGenerator.pas

@@ -0,0 +1,168 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpCryptoApiRandomGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SyncObjs,
+  ClpCryptoLibTypes,
+  ClpIRandomNumberGenerator,
+  ClpRandomNumberGenerator,
+  ClpICryptoApiRandomGenerator,
+  ClpIRandomGenerator;
+
+resourcestring
+  SNegativeOffset = 'Start Offset Cannot be Negative, "Start"';
+  SArrayTooSmall = 'Byte Array Too Small For Requested Offset and Length';
+
+type
+  /// <summary>
+  /// Uses TRandomNumberGenerator.Create() to Get randomness generator
+  /// </summary>
+  TCryptoApiRandomGenerator = class(TInterfacedObject,
+    ICryptoApiRandomGenerator, IRandomGenerator)
+
+  strict private
+  var
+    FrndProv: IRandomNumberGenerator;
+
+    class var
+
+      FLock: TCriticalSection;
+
+    class procedure Boot(); static;
+    class constructor CreateCryptoApiRandomGenerator();
+    class destructor DestroyCryptoApiRandomGenerator();
+
+  public
+    /// <summary>
+    /// Uses TRandomNumberGenerator.CreateRNG() to Get randomness generator
+    /// </summary>
+    constructor Create(); overload;
+    constructor Create(const rng: IRandomNumberGenerator); overload;
+
+    /// <summary>Add more seed material to the generator.</summary>
+    /// <param name="seed">A byte array to be mixed into the generator's state.</param>
+    procedure AddSeedMaterial(const seed: TCryptoLibByteArray);
+      overload; virtual;
+
+    /// <summary>Add more seed material to the generator.</summary>
+    /// <param name="seed">A long value to be mixed into the generator's state.</param>
+    procedure AddSeedMaterial(seed: Int64); overload; virtual;
+
+    /// <summary>Fill byte array with random values.</summary>
+    /// <param name="bytes">Array to be filled.</param>
+    procedure NextBytes(const bytes: TCryptoLibByteArray); overload; virtual;
+
+    /// <summary>Fill byte array with random values.</summary>
+    /// <param name="bytes">Array to receive bytes.</param>
+    /// <param name="start">Index to start filling at.</param>
+    /// <param name="len">Length of segment to fill.</param>
+    procedure NextBytes(const bytes: TCryptoLibByteArray; start, len: Int32);
+      overload; virtual;
+
+  end;
+
+implementation
+
+{ TCryptoApiRandomGenerator }
+
+procedure TCryptoApiRandomGenerator.AddSeedMaterial(seed: Int64);
+begin
+  // We don't care about the seed
+end;
+
+class procedure TCryptoApiRandomGenerator.Boot;
+begin
+  if FLock = Nil then
+  begin
+    FLock := TCriticalSection.Create;
+  end;
+end;
+
+procedure TCryptoApiRandomGenerator.AddSeedMaterial
+  (const seed: TCryptoLibByteArray);
+begin
+  // We don't care about the seed
+end;
+
+constructor TCryptoApiRandomGenerator.Create(const rng: IRandomNumberGenerator);
+begin
+  Inherited Create();
+  FrndProv := rng;
+end;
+
+class constructor TCryptoApiRandomGenerator.CreateCryptoApiRandomGenerator;
+begin
+  TCryptoApiRandomGenerator.Boot;
+end;
+
+class destructor TCryptoApiRandomGenerator.DestroyCryptoApiRandomGenerator;
+begin
+  FLock.Free;
+end;
+
+constructor TCryptoApiRandomGenerator.Create;
+begin
+  Create(TRandomNumberGenerator.CreateRNG());
+end;
+
+procedure TCryptoApiRandomGenerator.NextBytes(const bytes: TCryptoLibByteArray);
+begin
+
+  FLock.Acquire;
+  try
+    FrndProv.GetBytes(bytes);
+  finally
+    FLock.Release;
+  end;
+end;
+
+procedure TCryptoApiRandomGenerator.NextBytes(const bytes: TCryptoLibByteArray;
+  start, len: Int32);
+var
+  tmpBuf: TCryptoLibByteArray;
+begin
+  if (start < 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SNegativeOffset);
+  end;
+  if (System.Length(bytes) < (start + len)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SArrayTooSmall);
+
+  end;
+
+  if ((System.Length(bytes) = len) and (start = 0)) then
+  begin
+    NextBytes(bytes);
+  end
+  else
+  begin
+    System.SetLength(tmpBuf, len);
+    NextBytes(tmpBuf);
+
+    System.Move(tmpBuf[0], bytes[start], len * System.SizeOf(Byte));
+
+  end;
+end;
+
+end.

+ 221 - 0
src/libraries/cryptolib4pascal/ClpCryptoLibTypes.pas

@@ -0,0 +1,221 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpCryptoLibTypes;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils;
+
+type
+
+  PIInterface = ^IInterface;
+{$IFDEF FPC}
+  PUInt64 = ^UInt64;
+{$ENDIF FPC}
+  ECryptoLibException = class(Exception);
+  EInvalidCastCryptoLibException = class(EInvalidCast);
+  EArithmeticCryptoLibException = class(ECryptoLibException);
+  EInvalidOperationCryptoLibException = class(ECryptoLibException);
+  EInvalidParameterCryptoLibException = class(ECryptoLibException);
+  EIndexOutOfRangeCryptoLibException = class(ECryptoLibException);
+  EArgumentCryptoLibException = class(ECryptoLibException);
+  EArgumentNilCryptoLibException = class(ECryptoLibException);
+  EArgumentOutOfRangeCryptoLibException = class(ECryptoLibException);
+  ENullReferenceCryptoLibException = class(ECryptoLibException);
+  EUnsupportedTypeCryptoLibException = class(ECryptoLibException);
+  EIOCryptoLibException = class(ECryptoLibException);
+  EFormatCryptoLibException = class(ECryptoLibException);
+  ENotImplementedCryptoLibException = class(ECryptoLibException);
+  ENotSupportedCryptoLibException = class(ECryptoLibException);
+  EEndOfStreamCryptoLibException = class(EIOCryptoLibException);
+  EStreamOverflowCryptoLibException = class(ECryptoLibException);
+  EAsn1CryptoLibException = class(ECryptoLibException);
+  EAsn1ParsingCryptoLibException = class(ECryptoLibException);
+  EInvalidKeyCryptoLibException = class(ECryptoLibException);
+  EInvalidCipherTextCryptoLibException = class(ECryptoLibException);
+  EStreamCryptoLibException = class(ECryptoLibException);
+  ESecurityUtilityCryptoLibException = class(ECryptoLibException);
+  EAccessCryptoLibException = class(ECryptoLibException);
+  EDataLengthCryptoLibException = class(ECryptoLibException);
+  EOutputLengthCryptoLibException = class(ECryptoLibException);
+  EBadBlockCryptoLibException = class(ECryptoLibException);
+
+  /// <summary>
+  /// Represents a dynamic array of Byte.
+  /// </summary>
+  TCryptoLibByteArray = TBytes;
+
+  /// <summary>
+  /// Represents a dynamic generic array of Type T.
+  /// </summary>
+  TCryptoLibGenericArray<T> = array of T;
+
+  /// <summary>
+  /// Represents a dynamic generic array of array of Type T.
+  /// </summary>
+  TCryptoLibMatrixGenericArray<T> = array of TCryptoLibGenericArray<T>;
+
+{$IFDEF DELPHIXE_UP}
+  /// <summary>
+  /// Represents a dynamic array of Boolean.
+  /// </summary>
+  TCryptoLibBooleanArray = TArray<Boolean>;
+
+  /// <summary>
+  /// Represents a dynamic array of ShortInt.
+  /// </summary>
+  TCryptoLibShortIntArray = TArray<ShortInt>;
+
+  /// <summary>
+  /// Represents a dynamic array of Int32.
+  /// </summary>
+  TCryptoLibInt32Array = TArray<Int32>;
+
+  /// <summary>
+  /// Represents a dynamic array of Int64.
+  /// </summary>
+  TCryptoLibInt64Array = TArray<Int64>;
+
+  /// <summary>
+  /// Represents a dynamic array of UInt32.
+  /// </summary>
+  TCryptoLibUInt32Array = TArray<UInt32>;
+
+  /// <summary>
+  /// Represents a dynamic array of UInt64.
+  /// </summary>
+  TCryptoLibUInt64Array = TArray<UInt64>;
+
+  /// <summary>
+  /// Represents a dynamic array of String.
+  /// </summary>
+  TCryptoLibStringArray = TArray<String>;
+
+  /// <summary>
+  /// Represents a dynamic array of Char.
+  /// </summary>
+  TCryptoLibCharArray = TArray<Char>;
+
+  /// <summary>
+  /// Represents a dynamic array of array of ShortInt.
+  /// </summary>
+  TCryptoLibMatrixShortIntArray = TArray<TCryptoLibShortIntArray>;
+
+  /// <summary>
+  /// Represents a dynamic array of array of byte.
+  /// </summary>
+  TCryptoLibMatrixByteArray = TArray<TCryptoLibByteArray>;
+
+  /// <summary>
+  /// Represents a dynamic array of array of Int32.
+  /// </summary>
+  TCryptoLibMatrixInt32Array = TArray<TCryptoLibInt32Array>;
+
+  /// <summary>
+  /// Represents a dynamic array of array of UInt32.
+  /// </summary>
+  TCryptoLibMatrixUInt32Array = TArray<TCryptoLibUInt32Array>;
+
+  /// <summary>
+  /// Represents a dynamic array of array of UInt64.
+  /// </summary>
+  TCryptoLibMatrixUInt64Array = TArray<TCryptoLibUInt64Array>;
+
+{$ELSE}
+  /// <summary>
+  /// Represents a dynamic array of Boolean.
+  /// </summary>
+  TCryptoLibBooleanArray = array of Boolean;
+
+  /// <summary>
+  /// Represents a dynamic array of ShortInt.
+  /// </summary>
+  TCryptoLibShortIntArray = array of ShortInt;
+
+  /// <summary>
+  /// Represents a dynamic array of Int32.
+  /// </summary>
+  TCryptoLibInt32Array = array of Int32;
+
+  /// <summary>
+  /// Represents a dynamic array of Int64.
+  /// </summary>
+  TCryptoLibInt64Array = array of Int64;
+
+  /// <summary>
+  /// Represents a dynamic array of UInt32.
+  /// </summary>
+  TCryptoLibUInt32Array = array of UInt32;
+
+  /// <summary>
+  /// Represents a dynamic array of UInt64.
+  /// </summary>
+  TCryptoLibUInt64Array = array of UInt64;
+
+  /// <summary>
+  /// Represents a dynamic array of String.
+  /// </summary>
+  TCryptoLibStringArray = array of String;
+
+  /// <summary>
+  /// Represents a dynamic array of Char.
+  /// </summary>
+  TCryptoLibCharArray = array of Char;
+
+  /// <summary>
+  /// Represents a dynamic array of array of ShortInt.
+  /// </summary>
+  TCryptoLibMatrixShortIntArray = array of TCryptoLibShortIntArray;
+
+  /// <summary>
+  /// Represents a dynamic array of array of byte.
+  /// </summary>
+  TCryptoLibMatrixByteArray = array of TCryptoLibByteArray;
+
+  /// <summary>
+  /// Represents a dynamic array of array of Int32.
+  /// </summary>
+  TCryptoLibMatrixInt32Array = array of TCryptoLibInt32Array;
+
+  /// <summary>
+  /// Represents a dynamic array of array of UInt32.
+  /// </summary>
+  TCryptoLibMatrixUInt32Array = array of TCryptoLibUInt32Array;
+
+  /// <summary>
+  /// Represents a dynamic array of array of UInt64.
+  /// </summary>
+  TCryptoLibMatrixUInt64Array = array of TCryptoLibUInt64Array;
+{$ENDIF DELPHIXE_UP}
+
+implementation
+
+{$IFDEF FPC}
+
+initialization
+
+// Set UTF-8 in AnsiStrings, just like Lazarus
+SetMultiByteConversionCodePage(CP_UTF8);
+// SetMultiByteFileSystemCodePage(CP_UTF8); not needed, this is the default under Windows
+SetMultiByteRTLFileSystemCodePage(CP_UTF8);
+{$ENDIF FPC}
+
+end.

+ 361 - 0
src/libraries/cryptolib4pascal/ClpCryptoProObjectIdentifiers.pas

@@ -0,0 +1,361 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpCryptoProObjectIdentifiers;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpAsn1Objects,
+  ClpIAsn1Objects;
+
+type
+  TCryptoProObjectIdentifiers = class sealed(TObject)
+
+  strict private
+
+  const
+    // GOST Algorithms OBJECT IDENTIFIERS :
+    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2)}
+    GostID: String = '1.2.643.2.2';
+
+  class var
+
+    FIsBooted: Boolean;
+    FGostR3411, FGostR3410x2001, FGostR3410x94, FGostR28147Cbc,
+      FID_Gost28147_89_CryptoPro_A_ParamSet, FGostR3411x94CryptoProParam,
+      FGostR3411Hmac, FGostR3410x2001CryptoProA, FGostElSgDH3410Default,
+      FGostR3410x94CryptoProXchB, FGostR3410x94CryptoProXchC,
+      FGostR3411x94WithGostR3410x94, FGostR3410x94CryptoProXchA,
+      FGostR3410x94CryptoProB, FGostR3410x94CryptoProC, FGostR3410x94CryptoProA,
+      FGostR3410x94CryptoProD, FGostR3411x94CryptoProParamSet,
+      FGostR3410x2001CryptoProXchB, FGostR3411x94WithGostR3410x2001,
+      FGostR3410x2001CryptoProXchA, FGostR3410x2001CryptoProB,
+      FGostR3410x2001CryptoProC, FGostElSgDH3410x1: IDerObjectIdentifier;
+
+    class function GetGostR3411: IDerObjectIdentifier; static; inline;
+
+    class function GetGostR28147Cbc: IDerObjectIdentifier; static; inline;
+    class function GetGostR3410x94: IDerObjectIdentifier; static; inline;
+    class function GetGostR3411Hmac: IDerObjectIdentifier; static; inline;
+    class function GetID_Gost28147_89_CryptoPro_A_ParamSet
+      : IDerObjectIdentifier; static; inline;
+    class function GetGostR3410x2001: IDerObjectIdentifier; static; inline;
+
+    class function GetGostElSgDH3410Default: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostElSgDH3410x1: IDerObjectIdentifier; static; inline;
+    class function GetGostR3410x2001CryptoProA: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3410x2001CryptoProB: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3410x2001CryptoProC: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3410x2001CryptoProXchA: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3410x2001CryptoProXchB: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3410x94CryptoProA: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3410x94CryptoProB: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3410x94CryptoProC: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3410x94CryptoProD: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3410x94CryptoProXchA: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3410x94CryptoProXchB: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3410x94CryptoProXchC: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3411x94CryptoProParam: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3411x94WithGostR3410x2001: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3411x94WithGostR3410x94: IDerObjectIdentifier;
+      static; inline;
+    class function GetGostR3411x94CryptoProParamSet: IDerObjectIdentifier;
+      static; inline;
+
+    class constructor CryptoProObjectIdentifiers();
+
+  public
+
+    class property GostR3411: IDerObjectIdentifier read GetGostR3411;
+    class property GostR3411Hmac: IDerObjectIdentifier read GetGostR3411Hmac;
+    class property GostR28147Cbc: IDerObjectIdentifier read GetGostR28147Cbc;
+    class property ID_Gost28147_89_CryptoPro_A_ParamSet: IDerObjectIdentifier
+      read GetID_Gost28147_89_CryptoPro_A_ParamSet;
+    class property GostR3410x94: IDerObjectIdentifier read GetGostR3410x94;
+
+    class property GostR3410x2001: IDerObjectIdentifier read GetGostR3410x2001;
+    class property GostR3411x94WithGostR3410x94: IDerObjectIdentifier
+      read GetGostR3411x94WithGostR3410x94;
+    class property GostR3411x94WithGostR3410x2001: IDerObjectIdentifier
+      read GetGostR3411x94WithGostR3410x2001;
+    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) hashes(30) }
+    class property GostR3411x94CryptoProParamSet: IDerObjectIdentifier
+      read GetGostR3411x94CryptoProParamSet;
+    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) signs(32) }
+    class property GostR3410x94CryptoProA: IDerObjectIdentifier
+      read GetGostR3410x94CryptoProA;
+
+    class property GostR3410x94CryptoProB: IDerObjectIdentifier
+      read GetGostR3410x94CryptoProB;
+    class property GostR3410x94CryptoProC: IDerObjectIdentifier
+      read GetGostR3410x94CryptoProC;
+    class property GostR3410x94CryptoProD: IDerObjectIdentifier
+      read GetGostR3410x94CryptoProD;
+    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) exchanges(33) }
+    class property GostR3410x94CryptoProXchA: IDerObjectIdentifier
+      read GetGostR3410x94CryptoProXchA;
+    class property GostR3410x94CryptoProXchB: IDerObjectIdentifier
+      read GetGostR3410x94CryptoProXchB;
+    class property GostR3410x94CryptoProXchC: IDerObjectIdentifier
+      read GetGostR3410x94CryptoProXchC;
+    // { iso(1) member-body(2)ru(643) rans(2) cryptopro(2) ecc-signs(35) }
+    class property GostR3410x2001CryptoProA: IDerObjectIdentifier
+      read GetGostR3410x2001CryptoProA;
+    class property GostR3410x2001CryptoProB: IDerObjectIdentifier
+      read GetGostR3410x2001CryptoProB;
+    class property GostR3410x2001CryptoProC: IDerObjectIdentifier
+      read GetGostR3410x2001CryptoProC;
+    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) ecc-exchanges(36) }
+    class property GostR3410x2001CryptoProXchA: IDerObjectIdentifier
+      read GetGostR3410x2001CryptoProXchA;
+    class property GostR3410x2001CryptoProXchB: IDerObjectIdentifier
+      read GetGostR3410x2001CryptoProXchB;
+
+    class property GostElSgDH3410Default: IDerObjectIdentifier
+      read GetGostElSgDH3410Default;
+    class property GostElSgDH3410x1: IDerObjectIdentifier
+      read GetGostElSgDH3410x1;
+
+    class property GostR3411x94CryptoProParam: IDerObjectIdentifier
+      read GetGostR3411x94CryptoProParam;
+
+    class procedure Boot(); static;
+
+  end;
+
+implementation
+
+{ TCryptoProObjectIdentifiers }
+
+class function TCryptoProObjectIdentifiers.GetGostR3411: IDerObjectIdentifier;
+begin
+  result := FGostR3411;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostElSgDH3410Default
+  : IDerObjectIdentifier;
+begin
+  result := FGostElSgDH3410Default;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostElSgDH3410x1
+  : IDerObjectIdentifier;
+begin
+  result := FGostElSgDH3410x1;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR28147Cbc
+  : IDerObjectIdentifier;
+begin
+  result := FGostR28147Cbc;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x2001
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x2001;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x2001CryptoProA
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x2001CryptoProA;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x2001CryptoProB
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x2001CryptoProB;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x2001CryptoProC
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x2001CryptoProC;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x2001CryptoProXchA
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x2001CryptoProXchA;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x2001CryptoProXchB
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x2001CryptoProXchB;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x94
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x94;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x94CryptoProA
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x94CryptoProA;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x94CryptoProB
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x94CryptoProB;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x94CryptoProC
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x94CryptoProC;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x94CryptoProD
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x94CryptoProD;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x94CryptoProXchA
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x94CryptoProXchA;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x94CryptoProXchB
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x94CryptoProXchB;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3410x94CryptoProXchC
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3410x94CryptoProXchC;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3411Hmac
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3411Hmac;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3411x94CryptoProParam
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3411x94CryptoProParam;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3411x94CryptoProParamSet
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3411x94CryptoProParamSet;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3411x94WithGostR3410x2001
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3411x94WithGostR3410x2001;
+end;
+
+class function TCryptoProObjectIdentifiers.GetGostR3411x94WithGostR3410x94
+  : IDerObjectIdentifier;
+begin
+  result := FGostR3411x94WithGostR3410x94;
+end;
+
+class function TCryptoProObjectIdentifiers.
+  GetID_Gost28147_89_CryptoPro_A_ParamSet: IDerObjectIdentifier;
+begin
+  result := FID_Gost28147_89_CryptoPro_A_ParamSet;
+end;
+
+class procedure TCryptoProObjectIdentifiers.Boot;
+begin
+  if not FIsBooted then
+  begin
+    FGostR3411 := TDerObjectIdentifier.Create(GostID + '.9');
+    FGostR3411Hmac := TDerObjectIdentifier.Create(GostID + '.10');
+
+    FGostR28147Cbc := TDerObjectIdentifier.Create(GostID + '.21');
+
+    FID_Gost28147_89_CryptoPro_A_ParamSet := TDerObjectIdentifier.Create
+      (GostID + '.31.1');
+
+    FGostR3410x94 := TDerObjectIdentifier.Create(GostID + '.20');
+    FGostR3410x2001 := TDerObjectIdentifier.Create(GostID + '.19');
+    FGostR3411x94WithGostR3410x94 := TDerObjectIdentifier.Create(GostID + '.4');
+    FGostR3411x94WithGostR3410x2001 := TDerObjectIdentifier.Create
+      (GostID + '.3');
+
+    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) hashes(30) }
+    FGostR3411x94CryptoProParamSet := TDerObjectIdentifier.Create
+      (GostID + '.30.1');
+
+    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) signs(32) }
+    FGostR3410x94CryptoProA := TDerObjectIdentifier.Create(GostID + '.32.2');
+    FGostR3410x94CryptoProB := TDerObjectIdentifier.Create(GostID + '.32.3');
+    FGostR3410x94CryptoProC := TDerObjectIdentifier.Create(GostID + '.32.4');
+    FGostR3410x94CryptoProD := TDerObjectIdentifier.Create(GostID + '.32.5');
+
+    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) exchanges(33) }
+    FGostR3410x94CryptoProXchA := TDerObjectIdentifier.Create(GostID + '.33.1');
+    FGostR3410x94CryptoProXchB := TDerObjectIdentifier.Create(GostID + '.33.2');
+    FGostR3410x94CryptoProXchC := TDerObjectIdentifier.Create(GostID + '.33.3');
+
+    // { iso(1) member-body(2)ru(643) rans(2) cryptopro(2) ecc-signs(35) }
+    FGostR3410x2001CryptoProA := TDerObjectIdentifier.Create(GostID + '.35.1');
+    FGostR3410x2001CryptoProB := TDerObjectIdentifier.Create(GostID + '.35.2');
+    FGostR3410x2001CryptoProC := TDerObjectIdentifier.Create(GostID + '.35.3');
+
+    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) ecc-exchanges(36) }
+    FGostR3410x2001CryptoProXchA := TDerObjectIdentifier.Create
+      (GostID + '.36.0');
+    FGostR3410x2001CryptoProXchB := TDerObjectIdentifier.Create
+      (GostID + '.36.1');
+
+    FGostElSgDH3410Default := TDerObjectIdentifier.Create(GostID + '.36.0');
+    FGostElSgDH3410x1 := TDerObjectIdentifier.Create(GostID + '.36.1');
+
+    FIsBooted := True;
+  end;
+end;
+
+class constructor TCryptoProObjectIdentifiers.CryptoProObjectIdentifiers;
+begin
+  TCryptoProObjectIdentifiers.Boot;
+end;
+
+end.

+ 492 - 0
src/libraries/cryptolib4pascal/ClpCustomNamedCurves.pas

@@ -0,0 +1,492 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpCustomNamedCurves;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Generics.Collections,
+  ClpEncoders,
+  ClpGlvTypeBParameters,
+  ClpIGlvTypeBEndomorphism,
+  ClpSecObjectIdentifiers,
+  ClpCryptoLibTypes,
+  ClpBigInteger,
+  ClpECC,
+  ClpSecP256K1Custom,
+  ClpISecP256K1Custom,
+  ClpSecP256R1Custom,
+  ClpISecP256R1Custom,
+  ClpSecP384R1Custom,
+  ClpISecP384R1Custom,
+  ClpSecP521R1Custom,
+  ClpISecP521R1Custom,
+  ClpSecT283Custom,
+  ClpISecT283Custom,
+  ClpIECC,
+  ClpX9ECC,
+  ClpIX9ECC,
+  ClpIAsn1Objects,
+  ClpGlvTypeBEndomorphism,
+  ClpX9ECParameters,
+  ClpIX9ECParameters,
+  ClpX9ECParametersHolder,
+  ClpIX9ECParametersHolder,
+  ClpIGlvTypeBParameters;
+
+type
+  TCustomNamedCurves = class sealed(TObject)
+
+  strict private
+
+  class var
+    FnameToCurve: TDictionary<String, IX9ECParametersHolder>;
+    FnameToOid: TDictionary<String, IDerObjectIdentifier>;
+    FoidToCurve: TDictionary<IDerObjectIdentifier, IX9ECParametersHolder>;
+    FoidToName: TDictionary<IDerObjectIdentifier, String>;
+
+    Fnames: TList<String>;
+
+    class function GetNames: TCryptoLibStringArray; static; inline;
+
+    // class procedure DefineCurve(const name: String;
+    // const holder: IX9ECParametersHolder); static; inline;
+
+    class procedure DefineCurveWithOid(const name: String;
+      const oid: IDerObjectIdentifier; const holder: IX9ECParametersHolder);
+      static; inline;
+
+    class procedure DefineCurveAlias(const name: String;
+      const oid: IDerObjectIdentifier); static; inline;
+
+    class function ConfigureCurve(const curve: IECCurve): IECCurve;
+      static; inline;
+    class function ConfigureCurveGlv(const c: IECCurve;
+      const p: IGlvTypeBParameters): IECCurve; static; inline;
+
+    class procedure Boot(); static;
+    class constructor CreateCustomNamedCurves();
+    class destructor DestroyCustomNamedCurves();
+
+  public
+    class function GetByName(const name: String): IX9ECParameters;
+      static; inline;
+    // /**
+    // * return the X9ECParameters object for the named curve represented by
+    // * the passed in object identifier. Null if the curve isn't present.
+    // *
+    // * @param oid an object identifier representing a named curve, if present.
+    // */
+    class function GetByOid(const oid: IDerObjectIdentifier): IX9ECParameters;
+      static; inline;
+    // /**
+    // * return the object identifier signified by the passed in name. Null
+    // * if there is no object identifier associated with name.
+    // *
+    // * @return the object identifier associated with name, if present.
+    // */
+    class function GetOid(const name: String): IDerObjectIdentifier;
+      static; inline;
+    // /**
+    // * return the named curve name represented by the given object identifier.
+    // */
+    class function GetName(const oid: IDerObjectIdentifier): String;
+      static; inline;
+    // /**
+    // * returns an enumeration containing the name strings for curves
+    // * contained in this structure.
+    // */
+    class property Names: TCryptoLibStringArray read GetNames;
+
+  type
+
+    /// <summary>
+    /// secp256k1
+    /// </summary>
+    TSecP256K1Holder = class sealed(TX9ECParametersHolder,
+      IX9ECParametersHolder)
+
+    strict protected
+      function CreateParameters(): IX9ECParameters; override;
+
+    public
+      class function Instance(): IX9ECParametersHolder; static;
+
+    end;
+
+  type
+
+    /// <summary>
+    /// secp256r1
+    /// </summary>
+    TSecP256R1Holder = class sealed(TX9ECParametersHolder,
+      IX9ECParametersHolder)
+
+    strict protected
+      function CreateParameters(): IX9ECParameters; override;
+
+    public
+      class function Instance(): IX9ECParametersHolder; static;
+
+    end;
+
+  type
+
+    /// <summary>
+    /// secp384r1
+    /// </summary>
+    TSecP384R1Holder = class sealed(TX9ECParametersHolder,
+      IX9ECParametersHolder)
+
+    strict protected
+      function CreateParameters(): IX9ECParameters; override;
+
+    public
+      class function Instance(): IX9ECParametersHolder; static;
+
+    end;
+
+  type
+
+    /// <summary>
+    /// secp521r1
+    /// </summary>
+    TSecP521R1Holder = class sealed(TX9ECParametersHolder,
+      IX9ECParametersHolder)
+
+    strict protected
+      function CreateParameters(): IX9ECParameters; override;
+
+    public
+      class function Instance(): IX9ECParametersHolder; static;
+
+    end;
+
+  type
+
+    /// <summary>
+    /// sect283k1
+    /// </summary>
+    TSecT283K1Holder = class sealed(TX9ECParametersHolder,
+      IX9ECParametersHolder)
+
+    strict protected
+      function CreateParameters(): IX9ECParameters; override;
+
+    public
+      class function Instance(): IX9ECParametersHolder; static;
+
+    end;
+
+  end;
+
+implementation
+
+{ TCustomNamedCurves }
+
+// class procedure TCustomNamedCurves.DefineCurve(const name: String;
+// const holder: IX9ECParametersHolder);
+// var
+// LName: string;
+// begin
+// LName := name;
+// Fnames.Add(LName);
+// LName := UpperCase(LName);
+// FnameToCurve.Add(LName, holder);
+// end;
+
+class procedure TCustomNamedCurves.DefineCurveWithOid(const name: String;
+  const oid: IDerObjectIdentifier; const holder: IX9ECParametersHolder);
+var
+  LName: string;
+begin
+  LName := name;
+  Fnames.Add(LName);
+  FoidToName.Add(oid, LName);
+  FoidToCurve.Add(oid, holder);
+  LName := UpperCase(LName);
+  FnameToOid.Add(LName, oid);
+  FnameToCurve.Add(LName, holder);
+end;
+
+class procedure TCustomNamedCurves.DefineCurveAlias(const name: String;
+  const oid: IDerObjectIdentifier);
+var
+  curve: IX9ECParametersHolder;
+  LName: string;
+begin
+  LName := name;
+  if not(FoidToCurve.TryGetValue(oid, curve)) then
+  begin
+    raise EInvalidOperationCryptoLibException.Create('');
+  end;
+  LName := UpperCase(LName);
+  FnameToOid.Add(LName, oid);
+  FnameToCurve.Add(LName, curve);
+end;
+
+class function TCustomNamedCurves.ConfigureCurve(const curve: IECCurve)
+  : IECCurve;
+begin
+  result := curve;
+end;
+
+class function TCustomNamedCurves.ConfigureCurveGlv(const c: IECCurve;
+  const p: IGlvTypeBParameters): IECCurve;
+var
+  glv: IGlvTypeBEndomorphism;
+begin
+  glv := TGlvTypeBEndomorphism.Create(c, p);
+  result := c.Configure().SetEndomorphism(glv).CreateCurve();
+end;
+
+class function TCustomNamedCurves.GetByOid(const oid: IDerObjectIdentifier)
+  : IX9ECParameters;
+var
+  holder: IX9ECParametersHolder;
+begin
+  if FoidToCurve.TryGetValue(oid, holder) then
+  begin
+    result := holder.Parameters
+  end
+  else
+  begin
+    result := Nil;
+  end;
+end;
+
+class function TCustomNamedCurves.GetOid(const name: String)
+  : IDerObjectIdentifier;
+begin
+  if not(FnameToOid.TryGetValue(UpperCase(name), result)) then
+  begin
+    result := Nil;
+  end;
+end;
+
+class function TCustomNamedCurves.GetByName(const name: String)
+  : IX9ECParameters;
+var
+  holder: IX9ECParametersHolder;
+begin
+  if FnameToCurve.TryGetValue(UpperCase(name), holder) then
+  begin
+    result := holder.Parameters
+  end
+  else
+  begin
+    result := Nil;
+  end;
+end;
+
+class function TCustomNamedCurves.GetName
+  (const oid: IDerObjectIdentifier): String;
+begin
+  if not(FoidToName.TryGetValue(oid, result)) then
+  begin
+    result := '';
+  end;
+end;
+
+class function TCustomNamedCurves.GetNames: TCryptoLibStringArray;
+begin
+  result := Fnames.ToArray();
+end;
+
+class constructor TCustomNamedCurves.CreateCustomNamedCurves;
+begin
+  TCustomNamedCurves.Boot;
+end;
+
+class destructor TCustomNamedCurves.DestroyCustomNamedCurves;
+begin
+  FnameToCurve.Free;
+  FnameToOid.Free;
+  FoidToCurve.Free;
+  FoidToName.Free;
+  Fnames.Free;
+end;
+
+class procedure TCustomNamedCurves.Boot;
+begin
+  FnameToCurve := TDictionary<String, IX9ECParametersHolder>.Create();
+  FnameToOid := TDictionary<String, IDerObjectIdentifier>.Create();
+  FoidToCurve := TDictionary<IDerObjectIdentifier,
+    IX9ECParametersHolder>.Create();
+  FoidToName := TDictionary<IDerObjectIdentifier, String>.Create();
+
+  Fnames := TList<String>.Create();
+
+  DefineCurveWithOid('secp256k1', TSecObjectIdentifiers.SecP256k1,
+    TSecP256K1Holder.Instance);
+
+  DefineCurveWithOid('secp256r1', TSecObjectIdentifiers.SecP256r1,
+    TSecP256R1Holder.Instance);
+
+  DefineCurveWithOid('secp384r1', TSecObjectIdentifiers.SecP384r1,
+    TSecP384R1Holder.Instance);
+
+  DefineCurveWithOid('secp521r1', TSecObjectIdentifiers.SecP521r1,
+    TSecP521R1Holder.Instance);
+
+  DefineCurveWithOid('sect283k1', TSecObjectIdentifiers.SecT283k1,
+    TSecT283K1Holder.Instance);
+
+  DefineCurveAlias('K-283', TSecObjectIdentifiers.SecT283k1);
+
+  DefineCurveAlias('P-256', TSecObjectIdentifiers.SecP256r1);
+  DefineCurveAlias('P-384', TSecObjectIdentifiers.SecP384r1);
+  DefineCurveAlias('P-521', TSecObjectIdentifiers.SecP521r1);
+end;
+
+{ TCustomNamedCurves.TSecP256K1Holder }
+
+function TCustomNamedCurves.TSecP256K1Holder.CreateParameters: IX9ECParameters;
+var
+  curve: IECCurve;
+  G: IX9ECPoint;
+  S: TCryptoLibByteArray;
+  glv: IGlvTypeBParameters;
+begin
+  S := Nil;
+  glv := TGlvTypeBParameters.Create
+    (TBigInteger.Create
+    ('7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee', 16),
+    TBigInteger.Create
+    ('5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72', 16),
+    TCryptoLibGenericArray<TBigInteger>.Create
+    (TBigInteger.Create('3086d221a7d46bcde86c90e49284eb15', 16),
+    TBigInteger.Create('-e4437ed6010e88286f547fa90abfe4c3', 16)),
+    TCryptoLibGenericArray<TBigInteger>.Create
+    (TBigInteger.Create('114ca50f7a8e2f3f657c1108d9d44cfd8', 16),
+    TBigInteger.Create('3086d221a7d46bcde86c90e49284eb15', 16)),
+    TBigInteger.Create('3086d221a7d46bcde86c90e49284eb153dab', 16),
+    TBigInteger.Create('e4437ed6010e88286f547fa90abfe4c42212', 16), 272);
+  curve := ConfigureCurveGlv(TSecP256K1Curve.Create() as ISecP256K1Curve, glv);
+  G := TX9ECPoint.Create(curve,
+    THex.Decode('04' +
+    '79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798' +
+    '483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8'));
+  result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
+end;
+
+class function TCustomNamedCurves.TSecP256K1Holder.Instance
+  : IX9ECParametersHolder;
+begin
+  result := TSecP256K1Holder.Create();
+end;
+
+{ TCustomNamedCurves.TSecP384R1Holder }
+
+function TCustomNamedCurves.TSecP384R1Holder.CreateParameters: IX9ECParameters;
+var
+  S: TCryptoLibByteArray;
+  curve: IECCurve;
+  G: IX9ECPoint;
+begin
+  S := THex.Decode('A335926AA319A27A1D00896A6773A4827ACDAC73');
+  curve := ConfigureCurve(TSecP384R1Curve.Create() as ISecP384R1Curve);
+  G := TX9ECPoint.Create(curve,
+    THex.Decode('04' +
+    'AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7'
+    + '3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F')
+    );
+  result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
+end;
+
+class function TCustomNamedCurves.TSecP384R1Holder.Instance
+  : IX9ECParametersHolder;
+begin
+  result := TSecP384R1Holder.Create();
+end;
+
+{ TCustomNamedCurves.TSecP521R1Holder }
+
+function TCustomNamedCurves.TSecP521R1Holder.CreateParameters: IX9ECParameters;
+var
+  S: TCryptoLibByteArray;
+  curve: IECCurve;
+  G: IX9ECPoint;
+begin
+  S := THex.Decode('D09E8800291CB85396CC6717393284AAA0DA64BA');
+  curve := ConfigureCurve(TSecP521R1Curve.Create() as ISecP521R1Curve);
+  G := TX9ECPoint.Create(curve,
+    THex.Decode('04' +
+    '00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66'
+    + '011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650')
+    );
+  result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
+end;
+
+class function TCustomNamedCurves.TSecP521R1Holder.Instance
+  : IX9ECParametersHolder;
+begin
+  result := TSecP521R1Holder.Create();
+end;
+
+{ TCustomNamedCurves.TSecT283K1Holder }
+
+function TCustomNamedCurves.TSecT283K1Holder.CreateParameters: IX9ECParameters;
+var
+  S: TCryptoLibByteArray;
+  curve: IECCurve;
+  G: IX9ECPoint;
+begin
+  S := Nil;
+  curve := ConfigureCurve(TSecT283K1Curve.Create() as ISecT283K1Curve);
+  G := TX9ECPoint.Create(curve,
+    THex.Decode('04' +
+    '0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836' +
+    '01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259')
+    );
+  result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
+end;
+
+class function TCustomNamedCurves.TSecT283K1Holder.Instance
+  : IX9ECParametersHolder;
+begin
+  result := TSecT283K1Holder.Create();
+end;
+
+{ TCustomNamedCurves.TSecP256R1Holder }
+
+function TCustomNamedCurves.TSecP256R1Holder.CreateParameters: IX9ECParameters;
+var
+  S: TCryptoLibByteArray;
+  curve: IECCurve;
+  G: IX9ECPoint;
+begin
+  S := THex.Decode('C49D360886E704936A6678E1139D26B7819F7E90');
+  curve := ConfigureCurve(TSecP256R1Curve.Create() as ISecP256R1Curve);
+  G := TX9ECPoint.Create(curve,
+    THex.Decode('04' +
+    '6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296' +
+    '4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5'));
+  result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
+end;
+
+class function TCustomNamedCurves.TSecP256R1Holder.Instance
+  : IX9ECParametersHolder;
+begin
+  result := TSecP256R1Holder.Create();
+end;
+
+end.

+ 179 - 0
src/libraries/cryptolib4pascal/ClpDigest.pas

@@ -0,0 +1,179 @@
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDigest;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  HlpIHash,
+  ClpIDigest,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SOutputBufferTooShort = 'Output Buffer Too Short';
+
+type
+
+  /// <summary>
+  /// Hash Wrapper For the Proper Implementation in HashLib4Pascal
+  /// </summary>
+  TDigest = class sealed(TInterfacedObject, IDigest)
+
+  strict private
+  var
+    FHash: IHash;
+
+    function GetAlgorithmName: string; inline;
+
+    function DoFinal: TCryptoLibByteArray; overload;
+
+  public
+    constructor Create(const hash: IHash);
+
+    /// <summary>
+    /// Gets the Underlying <b>IHash</b> Instance
+    /// </summary>
+    function GetUnderlyingIHash: IHash; inline;
+
+    /// <summary>
+    /// the size, in bytes, of the digest produced by this message digest.
+    /// </summary>
+    function GetDigestSize(): Int32; inline;
+
+    /// <summary>
+    /// the size, in bytes, of the internal buffer used by this digest.
+    /// </summary>
+    function GetByteLength(): Int32; inline;
+
+    /// <summary>
+    /// update the message digest with a single byte.
+    /// </summary>
+    procedure Update(input: Byte);
+
+    /// <summary>
+    /// update the message digest with a block of bytes.
+    /// </summary>
+    /// <param name="input">
+    /// the byte array containing the data.
+    /// </param>
+    /// <param name="inOff">
+    /// the offset into the byte array where the data starts.
+    /// </param>
+    /// <param name="len">
+    /// the length of the data.
+    /// </param>
+    procedure BlockUpdate(const input: TCryptoLibByteArray; inOff, len: Int32);
+
+    /// <summary>
+    /// Close the digest, producing the final digest value. The doFinal call
+    /// leaves the digest reset.
+    /// </summary>
+    /// <param name="output">
+    /// the array the digest is to be copied into.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset into the out array the digest is to start at.
+    /// </param>
+    function DoFinal(const output: TCryptoLibByteArray; outOff: Int32)
+      : Int32; overload;
+
+    /// <summary>
+    /// Resets the digest back to it's initial state.
+    /// </summary>
+    procedure Reset();
+
+    /// <summary>
+    /// the algorithm name
+    /// </summary>
+    property AlgorithmName: String read GetAlgorithmName;
+
+  end;
+
+implementation
+
+{ TDigest }
+
+function TDigest.GetAlgorithmName: string;
+begin
+  result := FHash.Name;
+end;
+
+function TDigest.GetByteLength: Int32;
+begin
+  result := FHash.BlockSize;
+end;
+
+function TDigest.GetDigestSize: Int32;
+begin
+  result := FHash.HashSize;
+end;
+
+function TDigest.GetUnderlyingIHash: IHash;
+begin
+  result := FHash;
+end;
+
+procedure TDigest.Reset;
+begin
+  FHash.Initialize;
+end;
+
+procedure TDigest.BlockUpdate(const input: TCryptoLibByteArray;
+  inOff, len: Int32);
+begin
+  FHash.TransformBytes(input, inOff, len);
+end;
+
+constructor TDigest.Create(const hash: IHash);
+begin
+  Inherited Create();
+  FHash := hash;
+  FHash.Initialize;
+end;
+
+function TDigest.DoFinal(const output: TCryptoLibByteArray;
+  outOff: Int32): Int32;
+var
+  buf: TCryptoLibByteArray;
+begin
+
+  if (System.Length(output) - outOff) < GetDigestSize then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
+  end
+  else
+  begin
+    buf := DoFinal();
+    System.Move(buf[0], output[outOff], System.Length(buf) *
+      System.SizeOf(Byte));
+  end;
+  result := System.Length(buf);
+end;
+
+function TDigest.DoFinal: TCryptoLibByteArray;
+begin
+  result := FHash.TransformFinal.GetBytes();
+end;
+
+procedure TDigest.Update(input: Byte);
+begin
+  FHash.TransformUntyped(input, System.SizeOf(Byte));
+end;
+
+end.

+ 214 - 0
src/libraries/cryptolib4pascal/ClpDigestRandomGenerator.pas

@@ -0,0 +1,214 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDigestRandomGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SyncObjs,
+  ClpIDigest,
+  ClpConverters,
+  ClpCryptoLibTypes,
+  ClpIDigestRandomGenerator,
+  ClpIRandomGenerator;
+
+type
+  /// **
+  // * Random generation based on the digest with counter. Calling AddSeedMaterial will
+  // * always increase the entropy of the hash.
+  // * <p>
+  // * Internal access to the digest is synchronized so a single one of these can be shared.
+  // * </p>
+  // */
+  TDigestRandomGenerator = class sealed(TInterfacedObject,
+    IDigestRandomGenerator, IRandomGenerator)
+
+  strict private
+  const
+    CYCLE_COUNT = Int64(10);
+
+  var
+    FstateCounter, FseedCounter: Int64;
+    Fdigest: IDigest;
+    Fstate, Fseed: TCryptoLibByteArray;
+
+    procedure CycleSeed(); inline;
+    procedure GenerateState(); inline;
+    procedure DigestAddCounter(seedVal: Int64); inline;
+    procedure DigestUpdate(const inSeed: TCryptoLibByteArray); inline;
+    procedure DigestDoFinal(const result: TCryptoLibByteArray); inline;
+
+    class var
+
+      FLock: TCriticalSection;
+
+    class procedure Boot(); static;
+    class constructor CreateDigestRandomGenerator();
+    class destructor DestroyDigestRandomGenerator();
+
+  public
+
+    constructor Create(const digest: IDigest);
+    procedure AddSeedMaterial(const inSeed: TCryptoLibByteArray);
+      overload; inline;
+    procedure AddSeedMaterial(rSeed: Int64); overload; inline;
+    procedure NextBytes(const bytes: TCryptoLibByteArray); overload; inline;
+    procedure NextBytes(const bytes: TCryptoLibByteArray;
+      start, len: Int32); overload;
+
+  end;
+
+implementation
+
+{ TDigestRandomGenerator }
+
+procedure TDigestRandomGenerator.DigestAddCounter(seedVal: Int64);
+var
+  bytes: TCryptoLibByteArray;
+begin
+  System.SetLength(bytes, 8);
+  bytes := TConverters.ReadUInt64AsBytesLE(UInt64(seedVal));
+  Fdigest.BlockUpdate(bytes, 0, System.Length(bytes));
+end;
+
+procedure TDigestRandomGenerator.DigestUpdate(const inSeed
+  : TCryptoLibByteArray);
+begin
+  Fdigest.BlockUpdate(inSeed, 0, System.Length(inSeed));
+end;
+
+procedure TDigestRandomGenerator.DigestDoFinal(const result
+  : TCryptoLibByteArray);
+begin
+  Fdigest.DoFinal(result, 0);
+end;
+
+procedure TDigestRandomGenerator.AddSeedMaterial(rSeed: Int64);
+begin
+
+  FLock.Acquire;
+  try
+    DigestAddCounter(rSeed);
+    DigestUpdate(Fseed);
+    DigestDoFinal(Fseed);
+  finally
+    FLock.Release;
+  end;
+end;
+
+class procedure TDigestRandomGenerator.Boot;
+begin
+  if FLock = Nil then
+  begin
+    FLock := TCriticalSection.Create;
+  end;
+end;
+
+procedure TDigestRandomGenerator.AddSeedMaterial(const inSeed
+  : TCryptoLibByteArray);
+begin
+
+  FLock.Acquire;
+  try
+    DigestUpdate(inSeed);
+    DigestUpdate(Fseed);
+    DigestDoFinal(Fseed);
+  finally
+    FLock.Release;
+  end;
+end;
+
+constructor TDigestRandomGenerator.Create(const digest: IDigest);
+begin
+  Inherited Create();
+  Fdigest := digest;
+  System.SetLength(Fseed, digest.GetDigestSize);
+  FseedCounter := 1;
+  System.SetLength(Fstate, digest.GetDigestSize);
+  FstateCounter := 1;
+end;
+
+class constructor TDigestRandomGenerator.CreateDigestRandomGenerator;
+begin
+  TDigestRandomGenerator.Boot;
+end;
+
+procedure TDigestRandomGenerator.CycleSeed;
+begin
+  DigestUpdate(Fseed);
+  DigestAddCounter(FseedCounter);
+  System.Inc(FseedCounter);
+  DigestDoFinal(Fseed);
+end;
+
+class destructor TDigestRandomGenerator.DestroyDigestRandomGenerator;
+begin
+  FLock.Free;
+end;
+
+procedure TDigestRandomGenerator.GenerateState;
+begin
+  DigestAddCounter(FstateCounter);
+  System.Inc(FstateCounter);
+  DigestUpdate(Fstate);
+  DigestUpdate(Fseed);
+  DigestDoFinal(Fstate);
+
+  if ((FstateCounter mod CYCLE_COUNT) = 0) then
+  begin
+    CycleSeed();
+  end;
+end;
+
+procedure TDigestRandomGenerator.NextBytes(const bytes: TCryptoLibByteArray);
+begin
+  NextBytes(bytes, 0, System.Length(bytes));
+end;
+
+procedure TDigestRandomGenerator.NextBytes(const bytes: TCryptoLibByteArray;
+  start, len: Int32);
+var
+  stateOff, endPoint: Int32;
+  I: Integer;
+begin
+
+  FLock.Acquire;
+  try
+    stateOff := 0;
+    GenerateState();
+    endPoint := start + len;
+
+    for I := start to System.Pred(endPoint) do
+    begin
+      if (stateOff = System.Length(Fstate)) then
+      begin
+        GenerateState();
+        stateOff := 0;
+      end;
+      bytes[I] := Fstate[stateOff];
+      System.Inc(stateOff);
+    end;
+
+  finally
+    FLock.Release;
+  end;
+end;
+
+end.

+ 534 - 0
src/libraries/cryptolib4pascal/ClpDigestUtilities.pas

@@ -0,0 +1,534 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDigestUtilities;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  TypInfo,
+  Generics.Collections,
+  HlpHashFactory,
+  ClpIDigest,
+  ClpDigest,
+  ClpPkcsObjectIdentifiers,
+  ClpRosstandartObjectIdentifiers,
+  ClpOiwObjectIdentifiers,
+  ClpNistObjectIdentifiers,
+  ClpMiscObjectIdentifiers,
+  ClpTeleTrusTObjectIdentifiers,
+  ClpCryptoProObjectIdentifiers,
+  ClpCryptoLibTypes,
+  ClpIAsn1Objects;
+
+resourcestring
+  SMechanismNil = 'Mechanism Cannot be Nil';
+  SUnRecognizedDigest = '"Digest " %s not recognised.';
+
+type
+  TDigestUtilities = class sealed(TObject)
+
+  strict private
+
+  class var
+    Falgorithms: TDictionary<String, String>;
+    Foids: TDictionary<String, IDerObjectIdentifier>;
+
+  type
+{$SCOPEDENUMS ON}
+    TDigestAlgorithm = (BLAKE2B_160, BLAKE2B_256, BLAKE2B_384, BLAKE2B_512,
+      BLAKE2S_128, BLAKE2S_160, BLAKE2S_224, BLAKE2S_256, GOST3411,
+      GOST3411_2012_256, GOST3411_2012_512, MD2, MD4, MD5, NONE, RIPEMD128,
+      RIPEMD160, RIPEMD256, RIPEMD320, SHA_1, SHA_224, SHA_256, SHA_384,
+      SHA_512, SHA_512_224, SHA_512_256, SHA3_224, SHA3_256, SHA3_384, SHA3_512,
+      TIGER, WHIRLPOOL);
+{$SCOPEDENUMS OFF}
+  class procedure Boot(); static;
+  class constructor CreateDigestUtilities();
+  class destructor DestroyDigestUtilities();
+
+  public
+    /// <summary>
+    /// Returns a ObjectIdentifier for a given digest mechanism.
+    /// </summary>
+    /// <param name="mechanism">A string representation of the digest mechanism.</param>
+    /// <returns>A DerObjectIdentifier, null if the Oid is not available.</returns>
+    class function GetObjectIdentifier(mechanism: String)
+      : IDerObjectIdentifier; static;
+    class function GetDigest(const id: IDerObjectIdentifier): IDigest; overload;
+      static; inline;
+    class function GetDigest(const algorithm: String): IDigest;
+      overload; static;
+
+    class function GetAlgorithmName(const oid: IDerObjectIdentifier): String;
+      static; inline;
+
+    class function DoFinal(const digest: IDigest): TCryptoLibByteArray;
+      overload; static; inline;
+    class function DoFinal(const digest: IDigest;
+      const input: TCryptoLibByteArray): TCryptoLibByteArray; overload;
+      static; inline;
+
+    class function CalculateDigest(const algorithm: String;
+      const input: TCryptoLibByteArray): TCryptoLibByteArray; static; inline;
+
+  end;
+
+implementation
+
+{ TDigestUtilities }
+
+class function TDigestUtilities.GetDigest
+  (const id: IDerObjectIdentifier): IDigest;
+begin
+  result := GetDigest(id.id);
+end;
+
+class function TDigestUtilities.GetDigest(const algorithm: String): IDigest;
+var
+  upper, mechanism, temp: String;
+  digestAlgorithm: TDigestAlgorithm;
+begin
+
+  if (Falgorithms = Nil) or (Foids = Nil) then
+  begin
+    TDigestUtilities.Boot;
+  end;
+
+  upper := UpperCase(algorithm);
+  Falgorithms.TryGetValue(upper, mechanism);
+
+  if (mechanism = '') then
+  begin
+    mechanism := upper;
+  end;
+
+  temp := StringReplace(mechanism, '-', '_', [rfReplaceAll, rfIgnoreCase]);
+
+  temp := StringReplace(temp, '/', '_', [rfReplaceAll, rfIgnoreCase]);
+
+  digestAlgorithm := TDigestAlgorithm
+    (GetEnumValue(TypeInfo(TDigestAlgorithm), temp));
+
+  case digestAlgorithm of
+
+    TDigestAlgorithm.BLAKE2B_160:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateBlake2B_160);
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.BLAKE2B_256:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateBlake2B_256);
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.BLAKE2B_384:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateBlake2B_384);
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.BLAKE2B_512:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateBlake2B_512);
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.BLAKE2S_128:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateBlake2S_128);
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.BLAKE2S_160:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateBlake2S_160);
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.BLAKE2S_224:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateBlake2S_224);
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.BLAKE2S_256:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateBlake2S_256);
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.GOST3411:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateGost());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.GOST3411_2012_256:
+      begin
+        result := TDigest.Create
+          (THashFactory.TCrypto.CreateGOST3411_2012_256());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.GOST3411_2012_512:
+      begin
+        result := TDigest.Create
+          (THashFactory.TCrypto.CreateGOST3411_2012_512());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.MD2:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateMD2());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.MD4:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateMD4());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.MD5:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateMD5());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.NONE:
+      begin
+        result := TDigest.Create
+          (THashFactory.TNullDigestFactory.CreateNullDigest());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.RIPEMD128:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateRIPEMD128());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.RIPEMD160:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateRIPEMD160());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.RIPEMD256:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateRIPEMD256());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.RIPEMD320:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateRIPEMD320());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.SHA_1:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateSHA1());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.SHA_224:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateSHA2_224());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.SHA_256:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateSHA2_256());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.SHA_384:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateSHA2_384());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.SHA_512:
+      begin
+
+        result := TDigest.Create(THashFactory.TCrypto.CreateSHA2_512());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.SHA_512_224:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateSHA2_512_224());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.SHA_512_256:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateSHA2_512_256());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.SHA3_224:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateSHA3_224());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.SHA3_256:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateSHA3_256());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.SHA3_384:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateSHA3_384());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.SHA3_512:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateSHA3_512());
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.TIGER:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateTiger_3_192);
+
+        Exit;
+      end;
+
+    TDigestAlgorithm.WHIRLPOOL:
+      begin
+        result := TDigest.Create(THashFactory.TCrypto.CreateWhirlPool);
+
+        Exit;
+      end
+  else
+    begin
+      raise ESecurityUtilityCryptoLibException.CreateResFmt
+        (@SUnRecognizedDigest, [mechanism]);
+    end;
+
+  end;
+
+end;
+
+class procedure TDigestUtilities.Boot;
+begin
+  if (Falgorithms = Nil) or (Foids = Nil) then
+  begin
+    Falgorithms := TDictionary<string, string>.Create();
+    Foids := TDictionary<string, IDerObjectIdentifier>.Create();
+
+    Falgorithms.Add('NONE', 'NONE'); // Null Digest
+
+    TPkcsObjectIdentifiers.Boot;
+
+    Falgorithms.Add(TPkcsObjectIdentifiers.MD2.id, 'MD2');
+    Falgorithms.Add(TPkcsObjectIdentifiers.MD4.id, 'MD4');
+    Falgorithms.Add(TPkcsObjectIdentifiers.MD5.id, 'MD5');
+
+    TOiwObjectIdentifiers.Boot;
+
+    Falgorithms.Add('SHA1', 'SHA-1');
+    Falgorithms.Add(TOiwObjectIdentifiers.IdSha1.id, 'SHA-1');
+
+    TNistObjectIdentifiers.Boot;
+
+    Falgorithms.Add('SHA224', 'SHA-224');
+    Falgorithms.Add(TNistObjectIdentifiers.IdSha224.id, 'SHA-224');
+    Falgorithms.Add('SHA256', 'SHA-256');
+    Falgorithms.Add(TNistObjectIdentifiers.IdSha256.id, 'SHA-256');
+    Falgorithms.Add('SHA384', 'SHA-384');
+    Falgorithms.Add(TNistObjectIdentifiers.IdSha384.id, 'SHA-384');
+    Falgorithms.Add('SHA512', 'SHA-512');
+    Falgorithms.Add(TNistObjectIdentifiers.IdSha512.id, 'SHA-512');
+    Falgorithms.Add('SHA512/224', 'SHA-512/224');
+    Falgorithms.Add(TNistObjectIdentifiers.IdSha512_224.id, 'SHA-512/224');
+    Falgorithms.Add('SHA512/256', 'SHA-512/256');
+    Falgorithms.Add(TNistObjectIdentifiers.IdSha512_256.id, 'SHA-512/256');
+
+    TTeleTrusTObjectIdentifiers.Boot;
+
+    Falgorithms.Add('RIPEMD-128', 'RIPEMD128');
+    Falgorithms.Add(TTeleTrusTObjectIdentifiers.RIPEMD128.id, 'RIPEMD128');
+    Falgorithms.Add('RIPEMD-160', 'RIPEMD160');
+    Falgorithms.Add(TTeleTrusTObjectIdentifiers.RIPEMD160.id, 'RIPEMD160');
+    Falgorithms.Add('RIPEMD-256', 'RIPEMD256');
+    Falgorithms.Add(TTeleTrusTObjectIdentifiers.RIPEMD256.id, 'RIPEMD256');
+    Falgorithms.Add('RIPEMD-320', 'RIPEMD320');
+    // Falgorithms.Add(TTeleTrusTObjectIdentifiers.RipeMD320.Id,'RIPEMD320');
+
+    TCryptoProObjectIdentifiers.Boot;
+
+    Falgorithms.Add(TCryptoProObjectIdentifiers.GostR3411.id, 'GOST3411');
+
+    Falgorithms.Add(TNistObjectIdentifiers.IdSha3_224.id, 'SHA3-224');
+    Falgorithms.Add(TNistObjectIdentifiers.IdSha3_256.id, 'SHA3-256');
+    Falgorithms.Add(TNistObjectIdentifiers.IdSha3_384.id, 'SHA3-384');
+    Falgorithms.Add(TNistObjectIdentifiers.IdSha3_512.id, 'SHA3-512');
+
+    TMiscObjectIdentifiers.Boot;
+
+    Falgorithms.Add(TMiscObjectIdentifiers.id_blake2b160.id, 'BLAKE2B-160');
+    Falgorithms.Add(TMiscObjectIdentifiers.id_blake2b256.id, 'BLAKE2B-256');
+    Falgorithms.Add(TMiscObjectIdentifiers.id_blake2b384.id, 'BLAKE2B-384');
+    Falgorithms.Add(TMiscObjectIdentifiers.id_blake2b512.id, 'BLAKE2B-512');
+    Falgorithms.Add(TMiscObjectIdentifiers.id_blake2s128.id, 'BLAKE2S-128');
+    Falgorithms.Add(TMiscObjectIdentifiers.id_blake2s160.id, 'BLAKE2S-160');
+    Falgorithms.Add(TMiscObjectIdentifiers.id_blake2s224.id, 'BLAKE2S-224');
+    Falgorithms.Add(TMiscObjectIdentifiers.id_blake2s256.id, 'BLAKE2S-256');
+
+    TRosstandartObjectIdentifiers.Boot;
+
+    Falgorithms.Add(TRosstandartObjectIdentifiers.id_tc26_hmac_gost_3411_12_256.
+      id, 'HMAC-GOST3411-2012-256');
+    Falgorithms.Add(TRosstandartObjectIdentifiers.id_tc26_hmac_gost_3411_12_512.
+      id, 'HMAC-GOST3411-2012-512');
+
+    Foids.Add('MD2', TPkcsObjectIdentifiers.MD2);
+    Foids.Add('MD4', TPkcsObjectIdentifiers.MD4);
+    Foids.Add('MD5', TPkcsObjectIdentifiers.MD5);
+    Foids.Add('SHA-1', TOiwObjectIdentifiers.IdSha1);
+    Foids.Add('SHA-224', TNistObjectIdentifiers.IdSha224);
+    Foids.Add('SHA-256', TNistObjectIdentifiers.IdSha256);
+    Foids.Add('SHA-384', TNistObjectIdentifiers.IdSha384);
+    Foids.Add('SHA-512', TNistObjectIdentifiers.IdSha512);
+    Foids.Add('SHA-512/224', TNistObjectIdentifiers.IdSha512_224);
+    Foids.Add('SHA-512/256', TNistObjectIdentifiers.IdSha512_256);
+    Foids.Add('SHA3-224', TNistObjectIdentifiers.IdSha3_224);
+    Foids.Add('SHA3-256', TNistObjectIdentifiers.IdSha3_256);
+    Foids.Add('SHA3-384', TNistObjectIdentifiers.IdSha3_384);
+    Foids.Add('SHA3-512', TNistObjectIdentifiers.IdSha3_512);
+    Foids.Add('RIPEMD128', TTeleTrusTObjectIdentifiers.RIPEMD128);
+    Foids.Add('RIPEMD160', TTeleTrusTObjectIdentifiers.RIPEMD160);
+    Foids.Add('RIPEMD256', TTeleTrusTObjectIdentifiers.RIPEMD256);
+    Foids.Add('GOST3411', TCryptoProObjectIdentifiers.GostR3411);
+    Foids.Add('BLAKE2B-160', TMiscObjectIdentifiers.id_blake2b160);
+    Foids.Add('BLAKE2B-256', TMiscObjectIdentifiers.id_blake2b256);
+    Foids.Add('BLAKE2B-384', TMiscObjectIdentifiers.id_blake2b384);
+    Foids.Add('BLAKE2B-512', TMiscObjectIdentifiers.id_blake2b512);
+    Foids.Add('BLAKE2S-128', TMiscObjectIdentifiers.id_blake2s128);
+    Foids.Add('BLAKE2S-160', TMiscObjectIdentifiers.id_blake2s160);
+    Foids.Add('BLAKE2S-224', TMiscObjectIdentifiers.id_blake2s224);
+    Foids.Add('BLAKE2S-256', TMiscObjectIdentifiers.id_blake2s256);
+    Foids.Add('GOST3411-2012-256',
+      TRosstandartObjectIdentifiers.id_tc26_gost_3411_12_256);
+    Foids.Add('GOST3411-2012-512',
+      TRosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
+  end;
+end;
+
+class function TDigestUtilities.DoFinal(const digest: IDigest)
+  : TCryptoLibByteArray;
+begin
+  System.SetLength(result, digest.GetDigestSize());
+  digest.DoFinal(result, 0);
+end;
+
+class function TDigestUtilities.DoFinal(const digest: IDigest;
+  const input: TCryptoLibByteArray): TCryptoLibByteArray;
+begin
+  digest.BlockUpdate(input, 0, System.Length(input));
+  result := DoFinal(digest);
+end;
+
+class function TDigestUtilities.CalculateDigest(const algorithm: String;
+  const input: TCryptoLibByteArray): TCryptoLibByteArray;
+var
+  digest: IDigest;
+begin
+  digest := GetDigest(algorithm);
+  digest.BlockUpdate(input, 0, System.Length(input));
+  result := DoFinal(digest);
+end;
+
+class constructor TDigestUtilities.CreateDigestUtilities;
+begin
+  TDigestUtilities.Boot;
+end;
+
+class destructor TDigestUtilities.DestroyDigestUtilities;
+begin
+  Falgorithms.Free;
+  Foids.Free;
+end;
+
+class function TDigestUtilities.GetAlgorithmName
+  (const oid: IDerObjectIdentifier): String;
+begin
+  Falgorithms.TryGetValue(oid.id, result);
+end;
+
+class function TDigestUtilities.GetObjectIdentifier(mechanism: String)
+  : IDerObjectIdentifier;
+var
+  aliased: String;
+begin
+  if (mechanism = '') then
+    raise EArgumentNilCryptoLibException.CreateRes(@SMechanismNil);
+
+  mechanism := UpperCase(mechanism);
+  if Falgorithms.TryGetValue(mechanism, aliased) then
+  begin
+    mechanism := aliased;
+  end;
+
+  Foids.TryGetValue(mechanism, result);
+
+end;
+
+end.

+ 240 - 0
src/libraries/cryptolib4pascal/ClpDsaDigestSigner.pas

@@ -0,0 +1,240 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDsaDigestSigner;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+
+  SysUtils,
+  ClpIDsa,
+  ClpIDsaExt,
+  ClpIDigest,
+  ClpBigInteger,
+  ClpCryptoLibTypes,
+  ClpIParametersWithRandom,
+  ClpSignersEncodings,
+  ClpIAsymmetricKeyParameter,
+  ClpICipherParameters,
+  ClpISigner,
+  ClpISignersEncodings,
+  ClpIDsaDigestSigner;
+
+resourcestring
+  SPrivateKey = 'Signing Requires Private Key.';
+  SPublicKey = 'Verification Requires Public Key.';
+  SDsaDigestSignerNotInitializedForSignatureGeneration =
+    'DSADigestSigner not Initialized for Signature Generation.';
+  SDsaDigestSignerNotInitializedForVerification =
+    'DSADigestSigner not Initialized for Verification';
+  SEncodingError = 'Unable to Encode Signature';
+
+type
+  TDsaDigestSigner = class(TInterfacedObject, ISigner, IDsaDigestSigner)
+
+  strict private
+  var
+    Fdigest: IDigest;
+    Fdsa: IDsa;
+    Fencoding: IDsaEncoding;
+    FforSigning: Boolean;
+
+  strict protected
+
+    function GetOrder(): TBigInteger; virtual;
+
+  public
+    constructor Create(const dsa: IDsa; const digest: IDigest); overload;
+    constructor Create(const dsa: IDsaExt; const digest: IDigest;
+      const encoding: IDsaEncoding); overload;
+
+    function GetAlgorithmName: String; virtual;
+    property AlgorithmName: String read GetAlgorithmName;
+
+    procedure Init(forSigning: Boolean;
+      const parameters: ICipherParameters); virtual;
+
+    /// <summary>
+    /// update the internal digest with the byte b
+    /// </summary>
+    procedure Update(input: Byte); virtual;
+
+    /// <summary>
+    /// update the internal digest with the byte array in
+    /// </summary>
+    procedure BlockUpdate(const input: TCryptoLibByteArray;
+      inOff, length: Int32); virtual;
+
+    /// <summary>
+    /// Generate a signature for the message we've been loaded with using the
+    /// key we were initialised with.
+    /// </summary>
+    function GenerateSignature(): TCryptoLibByteArray; virtual;
+
+    /// <returns>
+    /// true if the internal state represents the signature described in the
+    /// passed in array.
+    /// </returns>
+    function VerifySignature(const signature: TCryptoLibByteArray)
+      : Boolean; virtual;
+
+    /// <summary>
+    /// Reset the internal state
+    /// </summary>
+    procedure Reset(); virtual;
+
+  end;
+
+implementation
+
+{ TDsaDigestSigner }
+
+procedure TDsaDigestSigner.BlockUpdate(const input: TCryptoLibByteArray;
+  inOff, length: Int32);
+begin
+  Fdigest.BlockUpdate(input, inOff, length);
+end;
+
+constructor TDsaDigestSigner.Create(const dsa: IDsa; const digest: IDigest);
+begin
+  Inherited Create();
+  Fdsa := dsa;
+  Fdigest := digest;
+  Fencoding := TStandardDsaEncoding.Instance;
+end;
+
+constructor TDsaDigestSigner.Create(const dsa: IDsaExt; const digest: IDigest;
+  const encoding: IDsaEncoding);
+begin
+  Inherited Create();
+  Fdsa := dsa;
+  Fdigest := digest;
+  Fencoding := encoding;
+end;
+
+function TDsaDigestSigner.GenerateSignature: TCryptoLibByteArray;
+var
+  hash: TCryptoLibByteArray;
+  sig: TCryptoLibGenericArray<TBigInteger>;
+begin
+  if ((not FforSigning)) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SDsaDigestSignerNotInitializedForSignatureGeneration);
+  end;
+
+  System.SetLength(hash, Fdigest.GetDigestSize);
+
+  Fdigest.DoFinal(hash, 0);
+
+  sig := Fdsa.GenerateSignature(hash);
+
+  try
+    Result := Fencoding.Encode(GetOrder(), sig[0], sig[1]);
+  except
+    raise EInvalidOperationCryptoLibException.CreateRes(@SEncodingError);
+  end;
+end;
+
+function TDsaDigestSigner.GetAlgorithmName: String;
+begin
+  Result := Fdigest.AlgorithmName + 'with' + Fdsa.AlgorithmName;
+end;
+
+function TDsaDigestSigner.GetOrder: TBigInteger;
+begin
+  if Supports(Fdsa, IDsaExt) then
+  begin
+    Result := (Fdsa as IDsaExt).Order;
+  end
+  else
+  begin
+    Result := Default (TBigInteger);
+  end;
+end;
+
+procedure TDsaDigestSigner.Init(forSigning: Boolean;
+  const parameters: ICipherParameters);
+var
+  k: IAsymmetricKeyParameter;
+  withRandom: IParametersWithRandom;
+begin
+  FforSigning := forSigning;
+
+  if (Supports(parameters, IParametersWithRandom, withRandom)) then
+  begin
+    k := withRandom.parameters as IAsymmetricKeyParameter;
+  end
+  else
+  begin
+    k := parameters as IAsymmetricKeyParameter;
+  end;
+
+  if ((forSigning) and (not k.IsPrivate)) then
+  begin
+    raise EInvalidKeyCryptoLibException.CreateRes(@SPrivateKey);
+  end;
+
+  if ((not forSigning) and (k.IsPrivate)) then
+  begin
+    raise EInvalidKeyCryptoLibException.CreateRes(@SPublicKey);
+  end;
+
+  Reset();
+
+  Fdsa.Init(forSigning, parameters);
+end;
+
+procedure TDsaDigestSigner.Reset;
+begin
+  Fdigest.Reset;
+end;
+
+procedure TDsaDigestSigner.Update(input: Byte);
+begin
+  Fdigest.Update(input);
+end;
+
+function TDsaDigestSigner.VerifySignature(const signature
+  : TCryptoLibByteArray): Boolean;
+var
+  hash: TCryptoLibByteArray;
+  sig: TCryptoLibGenericArray<TBigInteger>;
+begin
+  if (FforSigning) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SDsaDigestSignerNotInitializedForVerification);
+  end;
+
+  System.SetLength(hash, Fdigest.GetDigestSize);
+
+  Fdigest.DoFinal(hash, 0);
+
+  try
+    sig := Fencoding.Decode(GetOrder(), signature);
+    Result := Fdsa.VerifySignature(hash, sig[0], sig[1]);
+  except
+    Result := false;
+  end;
+
+end;
+
+end.

+ 66 - 0
src/libraries/cryptolib4pascal/ClpDsaKeyGenerationParameters.pas

@@ -0,0 +1,66 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDsaKeyGenerationParameters;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpBigInteger,
+  ClpISecureRandom,
+  ClpIDsaParameters,
+  ClpIDsaKeyGenerationParameters,
+  ClpKeyGenerationParameters;
+
+type
+  TDsaKeyGenerationParameters = class sealed(TKeyGenerationParameters,
+    IDsaKeyGenerationParameters)
+  strict private
+  var
+    Fparameters: IDsaParameters;
+
+    function GetParameters: IDsaParameters; inline;
+
+  public
+    constructor Create(const random: ISecureRandom;
+      const parameters: IDsaParameters);
+
+    property parameters: IDsaParameters read GetParameters;
+  end;
+
+implementation
+
+{ TDsaKeyGenerationParameters }
+
+constructor TDsaKeyGenerationParameters.Create(const random: ISecureRandom;
+  const parameters: IDsaParameters);
+var
+  P: TBigInteger;
+begin
+  P := parameters.P;
+  Inherited Create(random, P.BitLength - 1);
+  Fparameters := parameters;
+end;
+
+function TDsaKeyGenerationParameters.GetParameters: IDsaParameters;
+begin
+  result := Fparameters;
+end;
+
+end.

+ 142 - 0
src/libraries/cryptolib4pascal/ClpDsaKeyPairGenerator.pas

@@ -0,0 +1,142 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDsaKeyPairGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpISecureRandom,
+  ClpIDsaParameters,
+  ClpIDsaKeyPairGenerator,
+  ClpDsaPublicKeyParameters,
+  ClpIDsaPublicKeyParameters,
+  ClpDsaPrivateKeyParameters,
+  ClpIDsaPrivateKeyParameters,
+  ClpAsymmetricCipherKeyPair,
+  ClpIAsymmetricCipherKeyPair,
+  ClpIKeyGenerationParameters,
+  ClpIDsaKeyGenerationParameters,
+  ClpIAsymmetricCipherKeyPairGenerator,
+  ClpBits,
+  ClpBigInteger,
+  ClpBigIntegers,
+  ClpECAlgorithms,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SParametersCannotBeNil = '"parameters" Cannot Be Nil';
+
+type
+
+  /// <summary>
+  /// <para>
+  /// a DSA key pair generator.
+  /// </para>
+  /// <para>
+  /// This Generates DSA keys in line with the method described in <i>
+  /// FIPS 186-3 B.1 FFC Key Pair Generation</i>
+  /// </para>
+  /// </summary>
+  TDsaKeyPairGenerator = class sealed(TInterfacedObject,
+    IAsymmetricCipherKeyPairGenerator, IDsaKeyPairGenerator)
+
+  strict private
+
+  var
+    Fparam: IDsaKeyGenerationParameters;
+
+    class function GeneratePrivateKey(const q: TBigInteger;
+      const random: ISecureRandom): TBigInteger; static;
+
+    class function CalculatePublicKey(const p, g, x: TBigInteger): TBigInteger;
+      static; inline;
+
+  public
+
+    procedure Init(const parameters: IKeyGenerationParameters);
+
+    function GenerateKeyPair(): IAsymmetricCipherKeyPair;
+
+  end;
+
+implementation
+
+{ TDsaKeyPairGenerator }
+
+class function TDsaKeyPairGenerator.CalculatePublicKey(const p, g,
+  x: TBigInteger): TBigInteger;
+begin
+  result := g.ModPow(x, p);
+end;
+
+function TDsaKeyPairGenerator.GenerateKeyPair: IAsymmetricCipherKeyPair;
+var
+  dsaParams: IDsaParameters;
+  x, y: TBigInteger;
+begin
+  dsaParams := Fparam.parameters;
+
+  x := GeneratePrivateKey(dsaParams.q, Fparam.random);
+  y := CalculatePublicKey(dsaParams.p, dsaParams.g, x);
+
+  result := TAsymmetricCipherKeyPair.Create(TDsaPublicKeyParameters.Create(y,
+    dsaParams) as IDsaPublicKeyParameters, TDsaPrivateKeyParameters.Create(x,
+    dsaParams) as IDsaPrivateKeyParameters);
+end;
+
+class function TDsaKeyPairGenerator.GeneratePrivateKey(const q: TBigInteger;
+  const random: ISecureRandom): TBigInteger;
+var
+  minWeight: Int32;
+  x, One: TBigInteger;
+begin
+  One := TBigInteger.One;
+  result := Default (TBigInteger);
+  // B.1.2 Key Pair Generation by Testing Candidates
+  minWeight := TBits.Asr32(q.BitLength, 2);
+  while (True) do
+  begin
+    // TODO Prefer this method? (change test cases that used fixed random)
+    // B.1.1 Key Pair Generation Using Extra Random Bits
+    // x := TBigInteger.Create(q.BitLength + 64, random).&Mod(q.Subtract(One)).Add(One);
+
+    x := TBigIntegers.CreateRandomInRange(One, q.Subtract(One), random);
+    if (TWNafUtilities.GetNafWeight(x) >= minWeight) then
+    begin
+      result := x;
+      Exit;
+    end;
+  end;
+end;
+
+procedure TDsaKeyPairGenerator.Init(const parameters: IKeyGenerationParameters);
+begin
+  if (parameters = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SParametersCannotBeNil);
+  end;
+
+  // Note: If we start accepting instances of KeyGenerationParameters,
+  // must apply constraint checking on strength (see DsaParametersGenerator.Init)
+
+  Fparam := parameters as IDsaKeyGenerationParameters;
+end;
+
+end.

+ 93 - 0
src/libraries/cryptolib4pascal/ClpDsaKeyParameters.pas

@@ -0,0 +1,93 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDsaKeyParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDsaParameters,
+  ClpIDsaKeyParameters,
+  ClpAsymmetricKeyParameter;
+
+type
+  TDsaKeyParameters = class abstract(TAsymmetricKeyParameter, IDsaKeyParameters)
+
+  strict private
+  var
+    Fparameters: IDsaParameters;
+  strict protected
+  function GetParameters: IDsaParameters;
+    constructor Create(isPrivate: Boolean; parameters: IDsaParameters);
+
+  public
+    function Equals(const other: IDsaKeyParameters): Boolean; reintroduce; overload;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    property parameters: IDsaParameters read GetParameters;
+
+  end;
+
+implementation
+
+{ TDsaKeyParameters }
+
+constructor TDsaKeyParameters.Create(isPrivate: Boolean;
+  parameters: IDsaParameters);
+begin
+  Inherited Create(isPrivate);
+  // Note: parameters may be Nil
+  Fparameters := parameters;
+end;
+
+function TDsaKeyParameters.Equals(const other: IDsaKeyParameters): Boolean;
+begin
+  if other = Nil then
+  begin
+    result := False;
+    Exit;
+  end;
+  if ((Self as IDsaKeyParameters) = other) then
+  begin
+    result := True;
+    Exit;
+  end;
+  result := (parameters as TObject).Equals(other.parameters as TObject) and
+    (Inherited Equals(other));
+end;
+
+function TDsaKeyParameters.GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := Inherited GetHashCode();
+
+  if (parameters <> Nil) then
+  begin
+    result := result xor parameters.GetHashCode();
+  end;
+
+end;
+
+function TDsaKeyParameters.GetParameters: IDsaParameters;
+begin
+  result := Fparameters;
+end;
+
+end.

+ 136 - 0
src/libraries/cryptolib4pascal/ClpDsaParameter.pas

@@ -0,0 +1,136 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDsaParameter;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDsaParameter,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SBadSequenceSize = 'Bad Sequence Size "seq": %d';
+  SInvalidDsaParameter = 'Invalid DsaParameter: %s';
+
+type
+  TDsaParameter = class(TAsn1Encodable, IDsaParameter)
+
+  strict private
+  var
+    Fp, Fq, Fg: IDerInteger;
+
+    function GetG: TBigInteger; inline;
+    function GetP: TBigInteger; inline;
+    function GetQ: TBigInteger; inline;
+
+    constructor Create(const seq: IAsn1Sequence); overload;
+
+  public
+    constructor Create(const p, q, g: TBigInteger); overload;
+
+    function ToAsn1Object(): IAsn1Object; override;
+
+    property p: TBigInteger read GetP;
+    property q: TBigInteger read GetQ;
+    property g: TBigInteger read GetG;
+
+    class function GetInstance(const obj: IAsn1TaggedObject;
+      explicitly: Boolean): IDsaParameter; overload; static; inline;
+
+    class function GetInstance(obj: TObject): IDsaParameter; overload;
+      static; inline;
+
+  end;
+
+implementation
+
+{ TDsaParameter }
+
+function TDsaParameter.GetP: TBigInteger;
+begin
+  result := Fp.PositiveValue;
+end;
+
+function TDsaParameter.GetQ: TBigInteger;
+begin
+  result := Fq.PositiveValue;
+end;
+
+function TDsaParameter.GetG: TBigInteger;
+begin
+  result := Fg.PositiveValue;
+end;
+
+function TDsaParameter.ToAsn1Object: IAsn1Object;
+begin
+  result := TDerSequence.Create([Fp, Fq, Fg]);
+end;
+
+constructor TDsaParameter.Create(const seq: IAsn1Sequence);
+begin
+  Inherited Create();
+  if (seq.Count <> 3) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize,
+      [seq.Count]);
+  end;
+
+  Fp := TDerInteger.GetInstance(seq[0] as TAsn1Encodable);
+  Fq := TDerInteger.GetInstance(seq[1] as TAsn1Encodable);
+  Fg := TDerInteger.GetInstance(seq[2] as TAsn1Encodable);
+end;
+
+constructor TDsaParameter.Create(const p, q, g: TBigInteger);
+begin
+  Inherited Create();
+  Fp := TDerInteger.Create(p);
+  Fq := TDerInteger.Create(q);
+  Fg := TDerInteger.Create(g);
+end;
+
+class function TDsaParameter.GetInstance(obj: TObject): IDsaParameter;
+begin
+  if ((obj = Nil) or (obj is TDsaParameter)) then
+  begin
+    result := obj as TDsaParameter;
+    Exit;
+  end;
+
+  if (obj is TAsn1Sequence) then
+  begin
+    result := TDsaParameter.Create(obj as TAsn1Sequence);
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateResFmt(@SInvalidDsaParameter,
+    [obj.ToString]);
+end;
+
+class function TDsaParameter.GetInstance(const obj: IAsn1TaggedObject;
+  explicitly: Boolean): IDsaParameter;
+begin
+  result := GetInstance(TAsn1Sequence.GetInstance(obj, explicitly)
+    as TAsn1Sequence);
+end;
+
+end.

+ 146 - 0
src/libraries/cryptolib4pascal/ClpDsaParameterGenerationParameters.pas

@@ -0,0 +1,146 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDsaParameterGenerationParameters;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpISecureRandom,
+  ClpIDsaParameterGenerationParameters;
+
+type
+  TDsaParameterGenerationParameters = class(TInterfacedObject,
+    IDsaParameterGenerationParameters)
+
+  strict private
+  var
+    Fl, Fn, Fcertainty, FusageIndex: Int32;
+    Frandom: ISecureRandom;
+
+  strict protected
+
+    function GetL: Int32; virtual;
+    function GetN: Int32; virtual;
+    function GetCertainty: Int32; virtual;
+    function GetUsageIndex: Int32; virtual;
+    function GetRandom: ISecureRandom; virtual;
+
+  public
+
+    const
+    DigitalSignatureUsage = Int32(1);
+    KeyEstablishmentUsage = Int32(2);
+
+    /// <summary>
+    /// Construct without a usage index, this will do a random construction
+    /// of G.
+    /// </summary>
+    /// <param name="L">
+    /// desired length of prime P in bits (the effective key size).
+    /// </param>
+    /// <param name="N">
+    /// desired length of prime Q in bits.
+    /// </param>
+    /// <param name="certainty">
+    /// certainty level for prime number generation.
+    /// </param>
+    /// <param name="random">
+    /// the source of randomness to use.
+    /// </param>
+    constructor Create(L, N, certainty: Int32;
+      const random: ISecureRandom); overload;
+
+    /// <summary>
+    /// Construct without a usage index, this will do a random construction
+    /// of G.
+    /// </summary>
+    /// <param name="L">
+    /// desired length of prime P in bits (the effective key size).
+    /// </param>
+    /// <param name="N">
+    /// desired length of prime Q in bits.
+    /// </param>
+    /// <param name="certainty">
+    /// certainty level for prime number generation.
+    /// </param>
+    /// <param name="random">
+    /// the source of randomness to use.
+    /// </param>
+    /// <param name="usageIndex">
+    /// a valid usage index.
+    /// </param>
+    constructor Create(L, N, certainty: Int32; const random: ISecureRandom;
+      usageIndex: Int32); overload;
+
+    property L: Int32 read GetL;
+    property N: Int32 read GetN;
+    property usageIndex: Int32 read GetUsageIndex;
+    property certainty: Int32 read GetCertainty;
+    property random: ISecureRandom read GetRandom;
+
+  end;
+
+implementation
+
+{ TDsaParameterGenerationParameters }
+
+constructor TDsaParameterGenerationParameters.Create(L, N, certainty: Int32;
+  const random: ISecureRandom);
+begin
+  Create(L, N, certainty, random, -1);
+end;
+
+constructor TDsaParameterGenerationParameters.Create(L, N, certainty: Int32;
+  const random: ISecureRandom; usageIndex: Int32);
+begin
+  Inherited Create();
+  Fl := L;
+  Fn := N;
+  Fcertainty := certainty;
+  Frandom := random;
+  FusageIndex := usageIndex;
+end;
+
+function TDsaParameterGenerationParameters.GetL: Int32;
+begin
+  result := Fl;
+end;
+
+function TDsaParameterGenerationParameters.GetN: Int32;
+begin
+  result := Fn;
+end;
+
+function TDsaParameterGenerationParameters.GetCertainty: Int32;
+begin
+  result := Fcertainty;
+end;
+
+function TDsaParameterGenerationParameters.GetUsageIndex: Int32;
+begin
+  result := FusageIndex;
+end;
+
+function TDsaParameterGenerationParameters.GetRandom: ISecureRandom;
+begin
+  result := Frandom;
+end;
+
+end.

+ 170 - 0
src/libraries/cryptolib4pascal/ClpDsaParameters.pas

@@ -0,0 +1,170 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDsaParameters;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpICipherParameters,
+  ClpIDsaParameters,
+  ClpIDsaValidationParameters,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SPUnInitialized = '"P" Cannot Be Uninitialized';
+  SQUnInitialized = '"Q" Cannot Be Uninitialized';
+  SGUnInitialized = '"G" Cannot Be Uninitialized';
+
+type
+  TDsaParameters = class(TInterfacedObject, ICipherParameters, IDsaParameters)
+
+  strict private
+  var
+    Fp, Fq, Fg: TBigInteger;
+    Fvalidation: IDsaValidationParameters;
+
+    function GetG: TBigInteger; inline;
+    function GetP: TBigInteger; inline;
+    function GetQ: TBigInteger; inline;
+    function GetValidationParameters: IDsaValidationParameters; inline;
+
+  public
+
+    /// <summary>
+    /// Creates a new DSAParameter with the specified parameter values.
+    /// </summary>
+    /// <param name="p">
+    /// the prime.
+    /// </param>
+    /// <param name="q">
+    /// the sub-prime.
+    /// </param>
+    /// <param name="g">
+    /// the base.
+    /// </param>
+    constructor Create(const p, q, g: TBigInteger); overload;
+    /// <summary>
+    /// Creates a new DSAParameter with the specified parameter values.
+    /// </summary>
+    /// <param name="p">
+    /// the prime.
+    /// </param>
+    /// <param name="q">
+    /// the sub-prime.
+    /// </param>
+    /// <param name="g">
+    /// the base.
+    /// </param>
+    /// <param name="parameters">
+    /// dsa validation parameters (this includes the seed, counter and usage
+    /// index)
+    /// </param>
+    constructor Create(const p, q, g: TBigInteger;
+      const parameters: IDsaValidationParameters); overload;
+
+    function Equals(const other: IDsaParameters): Boolean; reintroduce;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    property p: TBigInteger read GetP;
+    property q: TBigInteger read GetQ;
+    property g: TBigInteger read GetG;
+    property ValidationParameters: IDsaValidationParameters
+      read GetValidationParameters;
+
+  end;
+
+implementation
+
+{ TDsaParameters }
+
+function TDsaParameters.GetG: TBigInteger;
+begin
+  result := Fg;
+end;
+
+function TDsaParameters.GetP: TBigInteger;
+begin
+  result := Fp;
+end;
+
+function TDsaParameters.GetQ: TBigInteger;
+begin
+  result := Fq;
+end;
+
+function TDsaParameters.GetValidationParameters: IDsaValidationParameters;
+begin
+  result := Fvalidation;
+end;
+
+constructor TDsaParameters.Create(const p, q, g: TBigInteger);
+begin
+  Create(p, q, g, Nil);
+end;
+
+constructor TDsaParameters.Create(const p, q, g: TBigInteger;
+  const parameters: IDsaValidationParameters);
+begin
+  Inherited Create();
+  if (not(p.IsInitialized)) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SPUnInitialized);
+  end;
+
+  if (not(q.IsInitialized)) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SQUnInitialized);
+  end;
+
+  if (not(g.IsInitialized)) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SGUnInitialized);
+  end;
+
+  Fp := p;
+  Fq := q;
+  Fg := g;
+  Fvalidation := parameters;
+end;
+
+function TDsaParameters.Equals(const other: IDsaParameters): Boolean;
+begin
+  if other = Nil then
+  begin
+    result := False;
+    Exit;
+  end;
+  if ((Self as IDsaParameters) = other) then
+  begin
+    result := True;
+    Exit;
+  end;
+  result := p.Equals(other.p) and q.Equals(other.q) and g.Equals(other.g);
+end;
+
+function TDsaParameters.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := p.GetHashCode() xor q.GetHashCode() xor g.GetHashCode();
+end;
+
+end.

+ 619 - 0
src/libraries/cryptolib4pascal/ClpDsaParametersGenerator.pas

@@ -0,0 +1,619 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDsaParametersGenerator;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Math,
+  SysUtils,
+  HlpSHA1,
+  ClpIDigest,
+  ClpISecureRandom,
+  ClpDsaParameters,
+  ClpIDsaParameters,
+  ClpDsaValidationParameters,
+  ClpIDsaValidationParameters,
+  ClpIDsaParametersGenerator,
+  ClpIDsaParameterGenerationParameters,
+  ClpEncoders,
+  ClpDigestUtilities,
+  ClpBigInteger,
+  ClpBigIntegers,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SInvalidLValue =
+    'L Values Must be Between 1024 and 3072 and a Multiple of 1024';
+  SInvalidNValueForSpecifiedL = 'N Must be " %d " for L = " %d "';
+  SInvalidNValueForSpecifiedL_Two = 'N Must be " %d " or " %d " for L = " %d "';
+  SDigestOutputSizeTooSmallForN =
+    'Digest Output Size Too Small for Value of N Which is " %d "';
+  SUnsupportedDigest =
+    'Can Only Use SHA-1 For Generating FIPS 186-2 Parameters';
+  SInvalidDsaKeyStrength =
+    'Size Must Be From %d - %d and a multiple of %d, "%d"';
+
+type
+
+  /// <summary>
+  /// Generate suitable parameters for DSA, in line with FIPS 186-2, or FIPS
+  /// 186-3.
+  /// </summary>
+  TDsaParametersGenerator = class(TInterfacedObject, IDsaParametersGenerator)
+
+  strict private
+    Fdigest: IDigest;
+    FL, FN, Fcertainty, Fiterations, FusageIndex: Int32;
+    Frandom: ISecureRandom;
+    Fuse186_3: Boolean;
+
+    function IsProbablePrime(const x: TBigInteger): Boolean; inline;
+
+    function GenerateParameters_FIPS186_2(): IDsaParameters;
+
+    /// <summary>
+    /// generate suitable parameters for DSA, in line with <i>FIPS 186-3 A.1
+    /// Generation of the FFC Primes p and q</i>
+    /// </summary>
+    function GenerateParameters_FIPS186_3(): IDsaParameters;
+
+    class function IsValidDsaStrength(strength: Int32): Boolean; static; inline;
+
+    class function GetDefaultN(L: Int32): Int32; static; inline;
+    class function GetMinimumIterations(L: Int32): Int32; static; inline;
+    class procedure Hash(const d: IDigest;
+      const input, output: TCryptoLibByteArray; outputPos: Int32);
+      static; inline;
+
+    class procedure Inc(const buf: TCryptoLibByteArray); static; inline;
+
+    class function CalculateGenerator_FIPS186_2(const p, q: TBigInteger;
+      const r: ISecureRandom): TBigInteger; static; inline;
+
+    class function CalculateGenerator_FIPS186_3_Unverifiable(const p,
+      q: TBigInteger; const r: ISecureRandom): TBigInteger; static; inline;
+
+    class function CalculateGenerator_FIPS186_3_Verifiable(const d: IDigest;
+      const p, q: TBigInteger; const seed: TCryptoLibByteArray; index: Int32)
+      : TBigInteger; static; inline;
+
+  public
+    constructor Create(); overload;
+    constructor Create(const digest: IDigest); overload;
+
+    /// <summary>
+    /// initialise the key generator.
+    /// </summary>
+    /// <param name="size">
+    /// size of the key (range 2^512 -&amp;gt; 2^1024 - 64 bit increments)
+    /// </param>
+    /// <param name="certainty">
+    /// measure of robustness of prime (for FIPS 186-2 compliance this should
+    /// be at least 80).
+    /// </param>
+    /// <param name="random">
+    /// random byte source.
+    /// </param>
+    procedure Init(size, certainty: Int32;
+      const random: ISecureRandom); overload;
+
+    /// <summary>
+    /// initialise the key generator.
+    /// </summary>
+    /// <param name="size">
+    /// size of the key (range 2^512 -&amp;gt; 2^1024 - 64 bit increments)
+    /// </param>
+    /// <param name="certainty">
+    /// measure of robustness of prime (for FIPS 186-2 compliance this should
+    /// be at least 80).
+    /// </param>
+    /// <param name="iterations">
+    /// iterations
+    /// </param>
+    /// <param name="random">
+    /// random byte source.
+    /// </param>
+    procedure Init(size, certainty, iterations: Int32;
+      const random: ISecureRandom); overload;
+
+    /// <summary>
+    /// <para>
+    /// Initialise the key generator for DSA 2.
+    /// </para>
+    /// <para>
+    /// Use this init method if you need to generate parameters for DSA 2
+    /// keys.
+    /// </para>
+    /// </summary>
+    /// <param name="params">
+    /// DSA 2 key generation parameters.
+    /// </param>
+    procedure Init(const params: IDsaParameterGenerationParameters); overload;
+
+    /// <summary>
+    /// <para>
+    /// which generates the p and g values from the given parameters,
+    /// returning the DSAParameters object.
+    /// </para>
+    /// <para>
+    /// Note: can take a while...
+    /// </para>
+    /// </summary>
+    /// <returns>
+    /// a generated DSA parameters object.
+    /// </returns>
+    function GenerateParameters(): IDsaParameters; inline;
+
+  end;
+
+implementation
+
+{ TDsaParametersGenerator }
+
+function TDsaParametersGenerator.IsProbablePrime(const x: TBigInteger): Boolean;
+begin
+  result := x.IsProbablePrime(Fcertainty);
+end;
+
+class function TDsaParametersGenerator.IsValidDsaStrength
+  (strength: Int32): Boolean;
+begin
+  // result := (strength >= 512) and (strength <= 1024) and ((strength mod 64) = 0);
+  result := (strength >= 512) and (strength <= 1024) and
+    ((strength and 63) = 0);
+end;
+
+class function TDsaParametersGenerator.GetDefaultN(L: Int32): Int32;
+begin
+  if L > 1024 then
+  begin
+    result := 256
+  end
+  else
+  begin
+    result := 160;
+  end;
+end;
+
+class function TDsaParametersGenerator.GetMinimumIterations(L: Int32): Int32;
+begin
+  // Values based on FIPS 186-4 C.3 Table C.1
+  if L <= 1024 then
+  begin
+    result := 40;
+  end
+  else
+  begin
+    result := (48 + 8 * ((L - 1) div 1024))
+  end;
+end;
+
+class procedure TDsaParametersGenerator.Hash(const d: IDigest;
+  const input, output: TCryptoLibByteArray; outputPos: Int32);
+begin
+  d.BlockUpdate(input, 0, System.Length(input));
+  d.DoFinal(output, outputPos);
+end;
+
+class procedure TDsaParametersGenerator.Inc(const buf: TCryptoLibByteArray);
+var
+  i: Int32;
+  b: Byte;
+begin
+  i := System.Length(buf) - 1;
+  while i >= 0 do
+  begin
+    b := Byte((buf[i] + 1) and $FF);
+    buf[i] := b;
+
+    if (b <> 0) then
+    begin
+      break;
+    end;
+    System.Dec(i);
+  end;
+end;
+
+constructor TDsaParametersGenerator.Create;
+begin
+  Create(TDigestUtilities.GetDigest('SHA-1'));
+end;
+
+class function TDsaParametersGenerator.CalculateGenerator_FIPS186_2(const p,
+  q: TBigInteger; const r: ISecureRandom): TBigInteger;
+var
+  e, pSub2, h, g: TBigInteger;
+begin
+  result := Default (TBigInteger);
+  e := p.subtract(TBigInteger.One).Divide(q);
+  pSub2 := p.subtract(TBigInteger.Two);
+
+  while True do
+  begin
+    h := TBigIntegers.CreateRandomInRange(TBigInteger.Two, pSub2, r);
+    g := h.ModPow(e, p);
+    if (g.BitLength > 1) then
+    begin
+      result := g;
+      Exit;
+    end;
+  end;
+end;
+
+class function TDsaParametersGenerator.CalculateGenerator_FIPS186_3_Unverifiable
+  (const p, q: TBigInteger; const r: ISecureRandom): TBigInteger;
+begin
+  result := CalculateGenerator_FIPS186_2(p, q, r);
+end;
+
+class function TDsaParametersGenerator.CalculateGenerator_FIPS186_3_Verifiable
+  (const d: IDigest; const p, q: TBigInteger; const seed: TCryptoLibByteArray;
+  index: Int32): TBigInteger;
+var
+  e, W, g: TBigInteger;
+  ggen, U, smallw: TCryptoLibByteArray;
+  count: Int32;
+begin
+  // A.2.3 Verifiable Canonical Generation of the Generator g
+  e := p.subtract(TBigInteger.One).Divide(q);
+  ggen := THex.Decode('6767656E');
+
+  // 7. U = domain_parameter_seed || "ggen" || index || count.
+  System.SetLength(U, System.Length(seed) + System.Length(ggen) + 1 + 2);
+  System.Move(seed[0], U[0], System.Length(seed) * System.SizeOf(Byte));
+  System.Move(ggen[0], U[System.Length(seed)], System.Length(ggen) *
+    System.SizeOf(Byte));
+  U[System.Length(U) - 3] := Byte(index);
+
+  System.SetLength(smallw, d.GetDigestSize());
+
+  count := 1;
+  while count < (1 shl 16) do
+  begin
+    Inc(U);
+    Hash(d, U, smallw, 0);
+    W := TBigInteger.Create(1, smallw);
+    g := W.ModPow(e, p);
+    if (g.CompareTo(TBigInteger.Two) >= 0) then
+    begin
+      result := g;
+      Exit;
+    end;
+    System.Inc(count);
+  end;
+
+  result := Default (TBigInteger);
+end;
+
+function TDsaParametersGenerator.GenerateParameters_FIPS186_2: IDsaParameters;
+var
+  seed, part1, part2, U, W, offset: TCryptoLibByteArray;
+  n, i, counter, k, remaining: Int32;
+  q, x, c, p, g: TBigInteger;
+begin
+  result := Nil;
+  System.SetLength(seed, 20);
+  System.SetLength(part1, 20);
+  System.SetLength(part2, 20);
+  System.SetLength(U, 20);
+  n := (FL - 1) div 160;
+  System.SetLength(W, FL div 8);
+
+  if (not {$IFDEF FPC} (Supports(Fdigest.GetUnderlyingIHash, TSHA1))
+{$ELSE} (Fdigest.GetUnderlyingIHash is TSHA1) {$ENDIF FPC}) then
+  begin
+    raise EInvalidParameterCryptoLibException.CreateRes(@SUnsupportedDigest);
+  end;
+
+  while True do
+  begin
+    Frandom.NextBytes(seed);
+
+    Hash(Fdigest, seed, part1, 0);
+    System.Move(seed[0], part2[0], System.Length(seed) * System.SizeOf(Byte));
+    Inc(part2);
+    Hash(Fdigest, part2, part2, 0);
+
+    i := 0;
+    while i <> System.Length(U) do
+    begin
+      U[i] := Byte(part1[i] xor part2[i]);
+      System.Inc(i);
+    end;
+
+    U[0] := U[0] or Byte($80);
+    U[19] := U[19] or Byte($01);
+
+    q := TBigInteger.Create(1, U);
+
+    if (not IsProbablePrime(q)) then
+    begin
+      continue;
+    end;
+
+    offset := System.Copy(seed);
+    Inc(offset);
+    counter := 0;
+    while counter < 4096 do
+    begin
+
+      k := 1;
+      while k <= n do
+      begin
+        Inc(offset);
+        Hash(Fdigest, offset, W, System.Length(W) - (k * System.Length(part1)));
+        System.Inc(k);
+      end;
+
+      remaining := System.Length(W) - (n * System.Length(part1));
+      Inc(offset);
+      Hash(Fdigest, offset, part1, 0);
+      System.Move(part1[System.Length(part1) - remaining], W[0], remaining);
+
+      W[0] := W[0] or Byte($80);
+
+      x := TBigInteger.Create(1, W);
+
+      c := x.&Mod(q.ShiftLeft(1));
+
+      p := x.subtract(c.subtract(TBigInteger.One));
+
+      if (p.BitLength <> FL) then
+      begin
+        System.Inc(counter);
+        continue;
+      end;
+
+      if (IsProbablePrime(p)) then
+      begin
+        g := CalculateGenerator_FIPS186_2(p, q, Frandom);
+
+        result := TDsaParameters.Create(p, q, g,
+          TDsaValidationParameters.Create(seed, counter)
+          as IDsaValidationParameters);
+        Exit;
+      end;
+
+      System.Inc(counter);
+    end;
+  end;
+end;
+
+function TDsaParametersGenerator.GenerateParameters_FIPS186_3: IDsaParameters;
+var
+  d: IDigest;
+  outlen, seedlen, n, { b, } counterLimit, counter, j, remaining: Int32;
+  seed, W, output, offset: TCryptoLibByteArray;
+  U, q, x, c, p, g: TBigInteger;
+begin
+  result := Nil;
+  // A.1.1.2 Generation of the Probable Primes p and q Using an Approved Hash Function
+  // TODO FIXME This should be configurable (digest size in bits must be >= N)
+  d := Fdigest;
+  outlen := d.GetDigestSize() * 8;
+
+  // 1. Check that the (L, N) pair is in the list of acceptable (L, N pairs) (see Section 4.2). If
+  // the pair is not in the list, then return INVALID.
+  // Note: checked at initialisation
+
+  // 2. If (seedlen < N), then return INVALID.
+  // TODO FIXME This should be configurable (must be >= N)
+  seedlen := FN;
+  System.SetLength(seed, seedlen div 8);
+
+  // 3. n = ceiling(L / outlen) - 1.
+  n := (FL - 1) div outlen;
+
+  // 4. b = L - 1 - (n * outlen).
+  // b := (FL - 1) mod outlen;
+
+  System.SetLength(W, FL div 8);
+
+  System.SetLength(output, d.GetDigestSize());
+
+  while True do
+  begin
+    // 5. Get an arbitrary sequence of seedlen bits as the domain_parameter_seed.
+    Frandom.NextBytes(seed);
+
+    // 6. U = Hash (domain_parameter_seed) mod 2^(N–1).
+    Hash(d, seed, output, 0);
+
+    U := TBigInteger.Create(1, output).&Mod(TBigInteger.One.ShiftLeft(FN - 1));
+
+    // 7. q = 2^(N–1) + U + 1 – ( U mod 2).
+    q := U.SetBit(0).SetBit(FN - 1);
+
+    // 8. Test whether or not q is prime as specified in Appendix C.3.
+    if (not IsProbablePrime(q)) then
+    begin
+      // 9. If q is not a prime, then go to step 5.
+      continue;
+    end;
+
+    // 10. offset = 1.
+    // Note: 'offset' value managed incrementally
+    offset := System.Copy(seed);
+
+    // 11. For counter = 0 to (4L – 1) do
+    counterLimit := 4 * FL;
+    counter := 0;
+    while counter < counterLimit do
+    begin
+      // 11.1 For j = 0 to n do
+      // Vj = Hash ((domain_parameter_seed + offset + j) mod 2^seedlen).
+      // 11.2 W = V0 + (V1 * 2^outlen) + ... + (V^(n–1) * 2^((n–1) * outlen)) + ((Vn mod 2^b) * 2^(n * outlen)).
+
+      j := 1;
+      while j <= n do
+      begin
+        Inc(offset);
+        Hash(d, offset, W, System.Length(W) - (j * System.Length(output)));
+        System.Inc(j);
+      end;
+
+      remaining := System.Length(W) - (n * System.Length(output));
+      Inc(offset);
+      Hash(d, offset, output, 0);
+      System.Move(output[System.Length(output) - remaining], W[0], remaining);
+
+      // 11.3 X = W + 2^(L–1). Comment: 0 ≤ W < 2^(L–1); hence, 2^(L–1) ≤ X < 2^L.
+      W[0] := W[0] or Byte($80);
+
+      x := TBigInteger.Create(1, W);
+
+      // 11.4 c = X mod 2q.
+      c := x.&Mod(q.ShiftLeft(1));
+
+      // 11.5 p = X - (c - 1). Comment: p ≡ 1 (mod 2q).
+      p := x.subtract(c.subtract(TBigInteger.One));
+
+      // 11.6 If (p < 2^(L-1)), then go to step 11.9
+      if (p.BitLength <> FL) then
+      begin
+        System.Inc(counter);
+        continue;
+      end;
+
+      // 11.7 Test whether or not p is prime as specified in Appendix C.3.
+      if (IsProbablePrime(p)) then
+      begin
+        // 11.8 If p is determined to be prime, then return VALID and the values of p, q and
+        // (optionally) the values of domain_parameter_seed and counter.
+        if (FusageIndex >= 0) then
+        begin
+          g := CalculateGenerator_FIPS186_3_Verifiable(d, p, q, seed,
+            FusageIndex);
+          if (g.IsInitialized) then
+          begin
+            result := TDsaParameters.Create(p, q, g,
+              TDsaValidationParameters.Create(seed, counter, FusageIndex)
+              as IDsaValidationParameters);
+            Exit;
+          end;
+        end;
+
+        g := CalculateGenerator_FIPS186_3_Unverifiable(p, q, Frandom);
+
+        result := TDsaParameters.Create(p, q, g,
+          TDsaValidationParameters.Create(seed, counter)
+          as IDsaValidationParameters);
+        Exit;
+      end;
+
+      // 11.9 offset = offset + n + 1.      Comment: Increment offset; then, as part of
+      // the loop in step 11, increment counter; if
+      // counter < 4L, repeat steps 11.1 through 11.8.
+      // Note: 'offset' value already incremented in inner loop
+      System.Inc(counter);
+    end;
+    // 12. Go to step 5.
+  end;
+
+end;
+
+function TDsaParametersGenerator.GenerateParameters: IDsaParameters;
+begin
+  if Fuse186_3 then
+  begin
+    result := GenerateParameters_FIPS186_3()
+  end
+  else
+  begin
+    result := GenerateParameters_FIPS186_2();
+  end;
+end;
+
+constructor TDsaParametersGenerator.Create(const digest: IDigest);
+begin
+  Inherited Create();
+  Fdigest := digest;
+end;
+
+procedure TDsaParametersGenerator.Init(size, certainty: Int32;
+  const random: ISecureRandom);
+begin
+  Init(size, certainty, Max(GetMinimumIterations(size),
+    (certainty + 1) div 2), random);
+end;
+
+procedure TDsaParametersGenerator.Init(size, certainty, iterations: Int32;
+  const random: ISecureRandom);
+begin
+  if (not IsValidDsaStrength(size)) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SInvalidDsaKeyStrength,
+      [512, 1024, 64, size]);
+  end;
+  FL := size;
+  FN := GetDefaultN(size);
+  Fcertainty := certainty;
+  Fiterations := iterations;
+  Frandom := random;
+  Fuse186_3 := false;
+  FusageIndex := -1;
+end;
+
+procedure TDsaParametersGenerator.Init(const params
+  : IDsaParameterGenerationParameters);
+var
+  L, n: Int32;
+begin
+  L := params.L;
+  n := params.n;
+
+  // if (((L < 1024) or (L > 3072)) or ((L mod 1024) <> 0))
+  if (((L < 1024) or (L > 3072)) or ((L and 1023) <> 0)) then
+  begin
+    raise EInvalidParameterCryptoLibException.CreateRes(@SInvalidLValue);
+  end
+  else if ((L = 1024) and (n <> 160)) then
+  begin
+    raise EInvalidParameterCryptoLibException.CreateResFmt
+      (@SInvalidNValueForSpecifiedL, [160, 1024]);
+  end
+  else if ((L = 2048) and ((n <> 224) and (n <> 256))) then
+  begin
+    raise EInvalidParameterCryptoLibException.CreateResFmt
+      (@SInvalidNValueForSpecifiedL_Two, [224, 256, 2048]);
+  end
+  else if ((L = 3072) and (n <> 256)) then
+  begin
+    raise EInvalidParameterCryptoLibException.CreateResFmt
+      (@SInvalidNValueForSpecifiedL, [256, 3072]);
+  end;
+
+  if ((Fdigest.GetDigestSize * 8) < n) then
+  begin
+    raise EInvalidParameterCryptoLibException.CreateResFmt
+      (@SDigestOutputSizeTooSmallForN, [n]);
+  end;
+
+  FL := L;
+  FN := n;
+  Fcertainty := params.certainty;
+  Fiterations := Max(GetMinimumIterations(L), (Fcertainty + 1) div 2);
+  Frandom := params.random;
+  Fuse186_3 := True;
+  FusageIndex := params.UsageIndex;
+end;
+
+end.

+ 98 - 0
src/libraries/cryptolib4pascal/ClpDsaPrivateKeyParameters.pas

@@ -0,0 +1,98 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDsaPrivateKeyParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDsaParameters,
+  ClpIDsaPrivateKeyParameters,
+  ClpDsaKeyParameters,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SXUnInitialized = '"X" Cannot Be Uninitialized';
+
+type
+  TDsaPrivateKeyParameters = class sealed(TDsaKeyParameters,
+    IDsaPrivateKeyParameters)
+
+  strict private
+  var
+    Fx: TBigInteger;
+
+    function GetX: TBigInteger; inline;
+
+  public
+    constructor Create(const x: TBigInteger; const parameters: IDsaParameters);
+
+    function Equals(const other: IDsaPrivateKeyParameters): Boolean;
+      reintroduce; overload;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    property x: TBigInteger read GetX;
+  end;
+
+implementation
+
+{ TDsaPrivateKeyParameters }
+
+function TDsaPrivateKeyParameters.GetX: TBigInteger;
+begin
+  result := Fx;
+end;
+
+constructor TDsaPrivateKeyParameters.Create(const x: TBigInteger;
+  const parameters: IDsaParameters);
+begin
+  Inherited Create(true, parameters);
+  if (not(x.IsInitialized)) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SXUnInitialized);
+  end;
+
+  Fx := x;
+end;
+
+function TDsaPrivateKeyParameters.Equals(const other
+  : IDsaPrivateKeyParameters): Boolean;
+begin
+  if other = Nil then
+  begin
+    result := False;
+    Exit;
+  end;
+  if ((Self as IDsaPrivateKeyParameters) = other) then
+  begin
+    result := true;
+    Exit;
+  end;
+  result := (x.Equals(other.x)) and (Inherited Equals(other));
+end;
+
+function TDsaPrivateKeyParameters.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := x.GetHashCode() xor (Inherited GetHashCode());
+end;
+
+end.

+ 118 - 0
src/libraries/cryptolib4pascal/ClpDsaPublicKeyParameters.pas

@@ -0,0 +1,118 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDsaPublicKeyParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDsaParameters,
+  ClpIDsaPublicKeyParameters,
+  ClpDsaKeyParameters,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SYUnInitialized = '"Y" Cannot Be Uninitialized';
+  SInvalidYInCorrectGroup = '"Y" Value Does Not Appear To Be In Correct Group';
+
+type
+  TDsaPublicKeyParameters = class sealed(TDsaKeyParameters,
+    IDsaPublicKeyParameters)
+
+  strict private
+  var
+    Fy: TBigInteger;
+
+    class function Validate(const y: TBigInteger;
+      const parameters: IDsaParameters): TBigInteger; static; inline;
+
+    function GetY: TBigInteger; inline;
+
+  public
+    constructor Create(const y: TBigInteger; const parameters: IDsaParameters);
+
+    function Equals(const other: IDsaPublicKeyParameters): Boolean; reintroduce; overload;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    property y: TBigInteger read GetY;
+  end;
+
+implementation
+
+{ TDsaPublicKeyParameters }
+
+function TDsaPublicKeyParameters.GetY: TBigInteger;
+begin
+  result := Fy;
+end;
+
+class function TDsaPublicKeyParameters.Validate(const y: TBigInteger;
+  const parameters: IDsaParameters): TBigInteger;
+begin
+  // we can't validate without params, fortunately we can't use the key either...
+  if (parameters <> Nil) then
+  begin
+    if ((y.CompareTo(TBigInteger.Two) < 0) or
+      (y.CompareTo(parameters.P.Subtract(TBigInteger.Two)) > 0) or
+      (not(y.ModPow(parameters.Q, parameters.P).Equals(TBigInteger.One)))) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SInvalidYInCorrectGroup);
+    end;
+  end;
+
+  result := y;
+end;
+
+constructor TDsaPublicKeyParameters.Create(const y: TBigInteger;
+  const parameters: IDsaParameters);
+begin
+  Inherited Create(false, parameters);
+  if (not(y.IsInitialized)) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SYUnInitialized);
+  end;
+
+  Fy := Validate(y, parameters);
+end;
+
+function TDsaPublicKeyParameters.Equals(const other
+  : IDsaPublicKeyParameters): Boolean;
+begin
+  if other = Nil then
+  begin
+    result := false;
+    Exit;
+  end;
+  if ((Self as IDsaPublicKeyParameters) = other) then
+  begin
+    result := True;
+    Exit;
+  end;
+  result := (y.Equals(other.y)) and (Inherited Equals(other));
+end;
+
+function TDsaPublicKeyParameters.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := y.GetHashCode() xor (Inherited GetHashCode());
+end;
+
+end.

+ 274 - 0
src/libraries/cryptolib4pascal/ClpDsaSigner.pas

@@ -0,0 +1,274 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDsaSigner;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Math,
+  SysUtils,
+  ClpIDsaExt,
+  ClpIDsaSigner,
+  ClpISecureRandom,
+  ClpIDsaParameters,
+  ClpIDsaKCalculator,
+  ClpICipherParameters,
+  ClpIDsaKeyParameters,
+  ClpIParametersWithRandom,
+  ClpIDsaPublicKeyParameters,
+  ClpIDsaPrivateKeyParameters,
+  ClpSecureRandom,
+  ClpRandomDsaKCalculator,
+  ClpBits,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SDSAPrivateKeyNotFound = 'DSA Private Key Required For Signing';
+  SDSAPublicKeyNotFound = 'DSA Public Key Required For Verification';
+
+type
+
+  /// <summary>
+  /// The Digital Signature Algorithm - as described in "Handbook of Applied <br />
+  /// Cryptography", pages 452 - 453.
+  /// </summary>
+  TDsaSigner = class(TInterfacedObject, IDsaExt, IDsaSigner)
+
+  strict private
+    function GetOrder: TBigInteger; virtual;
+    function GetAlgorithmName: String; virtual;
+  strict protected
+  var
+    FkCalculator: IDsaKCalculator;
+    Fkey: IDsaKeyParameters;
+    Frandom: ISecureRandom;
+
+    function CalculateE(const n: TBigInteger;
+      const &message: TCryptoLibByteArray): TBigInteger; virtual;
+
+    function InitSecureRandom(needed: Boolean; const provided: ISecureRandom)
+      : ISecureRandom; virtual;
+
+  public
+
+    /// <summary>
+    /// Default configuration, random K values.
+    /// </summary>
+    constructor Create(); overload;
+
+    /// <summary>
+    /// Configuration with an alternate, possibly deterministic calculator of
+    /// K.
+    /// </summary>
+    /// <param name="kCalculator">
+    /// a K value calculator.
+    /// </param>
+    constructor Create(const kCalculator: IDsaKCalculator); overload;
+
+    procedure Init(forSigning: Boolean; const parameters: ICipherParameters);
+
+    /// <summary>
+    /// Generate a signature for the given message using the key we were <br />
+    /// initialised with. For conventional DSA the message should be a SHA-1 <br />
+    /// hash of the message of interest.
+    /// </summary>
+    /// <param name="&amp;message">
+    /// the message that will be verified later.
+    /// </param>
+    function GenerateSignature(const &message: TCryptoLibByteArray)
+      : TCryptoLibGenericArray<TBigInteger>; virtual;
+
+    /// <summary>
+    /// return true if the value r and s represent a DSA signature for <br />
+    /// the passed in message for standard DSA the message should be a <br />
+    /// SHA-1 hash of the real message to be verified.
+    /// </summary>
+    function VerifySignature(const &message: TCryptoLibByteArray;
+      const r, s: TBigInteger): Boolean; virtual;
+
+    property Order: TBigInteger read GetOrder;
+    property AlgorithmName: String read GetAlgorithmName;
+
+  end;
+
+implementation
+
+{ TDsaSigner }
+
+constructor TDsaSigner.Create;
+begin
+  Inherited Create();
+  FkCalculator := TRandomDsaKCalculator.Create();
+end;
+
+function TDsaSigner.CalculateE(const n: TBigInteger;
+  const &message: TCryptoLibByteArray): TBigInteger;
+var
+  length: Int32;
+begin
+  length := Math.Min(System.length(&message), TBits.Asr32(n.BitLength, 3));
+  result := TBigInteger.Create(1, &message, 0, length);
+end;
+
+constructor TDsaSigner.Create(const kCalculator: IDsaKCalculator);
+begin
+  Inherited Create();
+  FkCalculator := kCalculator;
+end;
+
+function TDsaSigner.GenerateSignature(const &message: TCryptoLibByteArray)
+  : TCryptoLibGenericArray<TBigInteger>;
+var
+  parameters: IDsaParameters;
+  q, m, x, k, r, s: TBigInteger;
+begin
+  parameters := Fkey.parameters;
+  q := parameters.q;
+  m := CalculateE(q, &message);
+  x := (Fkey as IDsaPrivateKeyParameters).x;
+
+  if (FkCalculator.IsDeterministic) then
+  begin
+    FkCalculator.Init(q, x, &message);
+  end
+  else
+  begin
+    FkCalculator.Init(q, Frandom);
+  end;
+
+  k := FkCalculator.NextK();
+
+  r := parameters.G.ModPow(k, parameters.P).&Mod(q);
+
+  k := k.ModInverse(q).Multiply(m.Add(x.Multiply(r)));
+
+  s := k.&Mod(q);
+
+  result := TCryptoLibGenericArray<TBigInteger>.Create(r, s);
+end;
+
+function TDsaSigner.GetOrder: TBigInteger;
+begin
+  result := Fkey.parameters.q;
+end;
+
+function TDsaSigner.GetAlgorithmName: String;
+begin
+  result := 'DSA';
+end;
+
+procedure TDsaSigner.Init(forSigning: Boolean;
+  const parameters: ICipherParameters);
+var
+  providedRandom: ISecureRandom;
+  rParam: IParametersWithRandom;
+  Lparameters: ICipherParameters;
+begin
+  providedRandom := Nil;
+  Lparameters := parameters;
+
+  if (forSigning) then
+  begin
+    if (Supports(Lparameters, IParametersWithRandom, rParam)) then
+    begin
+      providedRandom := rParam.Random;
+      Lparameters := rParam.parameters;
+    end;
+
+    if (not Supports(Lparameters, IDsaPrivateKeyParameters)) then
+    begin
+      raise EInvalidKeyCryptoLibException.CreateRes(@SDSAPrivateKeyNotFound);
+    end;
+
+    Fkey := Lparameters as IDsaPrivateKeyParameters;
+  end
+  else
+  begin
+    if (not Supports(Lparameters, IDsaPublicKeyParameters)) then
+    begin
+      raise EInvalidKeyCryptoLibException.CreateRes(@SDSAPublicKeyNotFound);
+    end;
+
+    Fkey := Lparameters as IDsaPublicKeyParameters;
+  end;
+
+  Frandom := InitSecureRandom(forSigning and (not FkCalculator.IsDeterministic),
+    providedRandom);
+end;
+
+function TDsaSigner.InitSecureRandom(needed: Boolean;
+  const provided: ISecureRandom): ISecureRandom;
+begin
+  if (not needed) then
+  begin
+    result := Nil;
+  end
+  else
+  begin
+    if provided <> Nil then
+    begin
+      result := provided;
+    end
+    else
+    begin
+      result := TSecureRandom.Create();
+    end;
+  end;
+
+end;
+
+function TDsaSigner.VerifySignature(const &message: TCryptoLibByteArray;
+  const r, s: TBigInteger): Boolean;
+var
+  parameters: IDsaParameters;
+  q, m, w, u1, u2, P, v: TBigInteger;
+begin
+  parameters := Fkey.parameters;
+  q := parameters.q;
+  m := CalculateE(q, &message);
+
+  if ((r.SignValue <= 0) or (q.CompareTo(r) <= 0)) then
+  begin
+    result := false;
+    Exit;
+  end;
+
+  if ((s.SignValue <= 0) or (q.CompareTo(s) <= 0)) then
+  begin
+    result := false;
+    Exit;
+  end;
+
+  w := s.ModInverse(q);
+
+  u1 := m.Multiply(w).&Mod(q);
+  u2 := r.Multiply(w).&Mod(q);
+
+  P := parameters.P;
+  u1 := parameters.G.ModPow(u1, P);
+  u2 := (Fkey as IDsaPublicKeyParameters).Y.ModPow(u2, P);
+
+  v := u1.Multiply(u2).&Mod(P).&Mod(q);
+
+  result := v.Equals(r);
+end;
+
+end.

+ 121 - 0
src/libraries/cryptolib4pascal/ClpDsaValidationParameters.pas

@@ -0,0 +1,121 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDsaValidationParameters;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDsaValidationParameters,
+  ClpArrayUtils,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SSeedNil = '"Seed" Cannot Be Nil';
+
+type
+  TDsaValidationParameters = class(TInterfacedObject, IDsaValidationParameters)
+  strict private
+  var
+    Fseed: TCryptoLibByteArray;
+    Fcounter, FusageIndex: Int32;
+
+    function GetCounter: Int32; virtual;
+    function GetUsageIndex: Int32; virtual;
+    function GetSeed: TCryptoLibByteArray; virtual;
+
+  public
+    constructor Create(const seed: TCryptoLibByteArray;
+      counter: Int32); overload;
+    constructor Create(const seed: TCryptoLibByteArray;
+      counter, usageIndex: Int32); overload;
+
+    function Equals(const other: IDsaValidationParameters): Boolean;
+      reintroduce;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    property counter: Int32 read GetCounter;
+    property usageIndex: Int32 read GetUsageIndex;
+    property seed: TCryptoLibByteArray read GetSeed;
+  end;
+
+implementation
+
+{ TDsaValidationParameters }
+
+constructor TDsaValidationParameters.Create(const seed: TCryptoLibByteArray;
+  counter: Int32);
+begin
+  Create(seed, counter, -1);
+end;
+
+constructor TDsaValidationParameters.Create(const seed: TCryptoLibByteArray;
+  counter, usageIndex: Int32);
+begin
+  Inherited Create();
+  if (seed = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SSeedNil);
+  end;
+
+  Fseed := System.Copy(seed);
+  Fcounter := counter;
+  FusageIndex := usageIndex;
+end;
+
+function TDsaValidationParameters.Equals(const other
+  : IDsaValidationParameters): Boolean;
+begin
+  if other = Nil then
+  begin
+    result := False;
+    Exit;
+  end;
+  if ((Self as IDsaValidationParameters) = other) then
+  begin
+    result := True;
+    Exit;
+  end;
+  result := (counter = other.counter) and TArrayUtils.AreEqual(seed,
+    other.seed);
+end;
+
+function TDsaValidationParameters.GetCounter: Int32;
+begin
+  result := Fcounter;
+end;
+
+function TDsaValidationParameters.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := counter xor TArrayUtils.GetArrayHashCode(seed);
+end;
+
+function TDsaValidationParameters.GetSeed: TCryptoLibByteArray;
+begin
+  result := System.Copy(Fseed);
+end;
+
+function TDsaValidationParameters.GetUsageIndex: Int32;
+begin
+  result := FusageIndex;
+end;
+
+end.

+ 1800 - 0
src/libraries/cryptolib4pascal/ClpECAlgorithms.pas

@@ -0,0 +1,1800 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECAlgorithms;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Math,
+  ClpCryptoLibTypes,
+  ClpBits,
+  ClpBigInteger,
+  ClpWNafPreCompInfo,
+  ClpIPolynomialExtensionField,
+  ClpIGlvEndomorphism,
+  ClpIWNafPreCompInfo,
+  ClpIPreCompInfo,
+  ClpIPreCompCallBack,
+  ClpIECC,
+  ClpECCurveConstants,
+  ClpIFiniteField;
+
+resourcestring
+  SInvalidArray =
+    'Point and Scalar Arrays Should be Non-Null, and of Equal, Non-Zero, Length';
+  SInvalidPointLocation = 'Point Must be on the Same Curve';
+  SInvalidPoint = 'Invalid Point, "P"';
+  SInvalidResult = 'Invalid Result';
+  SInvalidRange = 'Must be in the Range [2, 16], "width"';
+  SInvalidRange2 = 'Must be in the Range [2, 8], "width"';
+
+type
+  TWNafUtilities = class abstract(TObject)
+
+  strict private
+  const
+    FDEFAULT_WINDOW_SIZE_CUTOFFS: array [0 .. 5] of Int32 = (13, 41, 121, 337,
+      897, 2305);
+
+  class var
+    FEMPTY_BYTES: TCryptoLibByteArray;
+    FEMPTY_INTS: TCryptoLibInt32Array;
+
+  type
+    IMapPointCallback = interface(IPreCompCallback)
+      ['{730BF27F-D5C3-4DF4-AC77-B8653C457C10}']
+
+    end;
+
+  type
+    TMapPointCallback = class(TInterfacedObject, IPreCompCallback,
+      IMapPointCallback)
+
+    strict private
+    var
+      Fm_wnafPreCompP: IWNafPreCompInfo;
+      Fm_includeNegated: Boolean;
+      Fm_pointMap: IECPointMap;
+
+    public
+      constructor Create(const wnafPreCompP: IWNafPreCompInfo;
+        includeNegated: Boolean; const pointMap: IECPointMap);
+
+      function Precompute(const existing: IPreCompInfo): IPreCompInfo;
+
+    end;
+
+  type
+    IWNafCallback = interface(IPreCompCallback)
+      ['{A439A606-7899-4720-937E-C2F3D94D4811}']
+
+    end;
+
+  type
+    TWNafCallback = class(TInterfacedObject, IPreCompCallback, IWNafCallback)
+
+    strict private
+
+    var
+      Fm_p: IECPoint;
+      Fm_width: Int32;
+      Fm_includeNegated: Boolean;
+
+    public
+      constructor Create(const p: IECPoint; width: Int32;
+        includeNegated: Boolean);
+
+      function Precompute(const existing: IPreCompInfo): IPreCompInfo;
+
+    end;
+
+  class function CheckExisting(const existingWNaf: IWNafPreCompInfo;
+    reqPreCompLen: Int32; includeNegated: Boolean): Boolean; static; inline;
+
+  class function CheckTable(const table: TCryptoLibGenericArray<IECPoint>;
+    reqLen: Int32): Boolean; static; inline;
+
+  class function Trim(const a: TCryptoLibByteArray; length: Int32)
+    : TCryptoLibByteArray; overload; static; inline;
+
+  class function Trim(const a: TCryptoLibInt32Array; length: Int32)
+    : TCryptoLibInt32Array; overload; static; inline;
+
+  class function ResizeTable(const a: TCryptoLibGenericArray<IECPoint>;
+    length: Int32): TCryptoLibGenericArray<IECPoint>; static; inline;
+
+  class procedure Boot(); static;
+  class constructor CreateWNafUtilities();
+
+  public
+
+    const
+    PRECOMP_NAME: String = 'bc_wnaf';
+
+    class function GenerateCompactNaf(const k: TBigInteger)
+      : TCryptoLibInt32Array; static;
+    class function GenerateCompactWindowNaf(width: Int32; const k: TBigInteger)
+      : TCryptoLibInt32Array; static;
+
+    class function GenerateJsf(const g, h: TBigInteger)
+      : TCryptoLibByteArray; static;
+    class function GenerateNaf(const k: TBigInteger)
+      : TCryptoLibByteArray; static;
+    // /**
+    // * Computes the Window NAF (non-adjacent Form) of an integer.
+    // * @param width The width <code>w</code> of the Window NAF. The width is
+    // * defined as the minimal number <code>w</code>, such that for any
+    // * <code>w</code> consecutive digits in the resulting representation, at
+    // * most one is non-zero.
+    // * @param k The integer of which the Window NAF is computed.
+    // * @return The Window NAF of the given width, such that the following holds:
+    // * <code>k = &amp;sum;<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup>
+    // * </code>, where the <code>k<sub>i</sub></code> denote the elements of the
+    // * returned <code>byte[]</code>.
+    // */
+    class function GenerateWindowNaf(width: Int32; const k: TBigInteger)
+      : TCryptoLibByteArray; static;
+
+    class function GetNafWeight(const k: TBigInteger): Int32; static; inline;
+
+    class function GetWNafPreCompInfo(const p: IECPoint): IWNafPreCompInfo;
+      overload; static; inline;
+
+    class function GetWNafPreCompInfo(const preCompInfo: IPreCompInfo)
+      : IWNafPreCompInfo; overload; static; inline;
+
+    /// <summary>
+    /// Determine window width to use for a scalar multiplication of the
+    /// given size.
+    /// </summary>
+    /// <param name="bits">
+    /// the bit-length of the scalar to multiply by
+    /// </param>
+    /// <returns>
+    /// the window size to use
+    /// </returns>
+    class function GetWindowSize(bits: Int32): Int32; overload; static; inline;
+
+    /// <summary>
+    /// Determine window width to use for a scalar multiplication of the
+    /// given size.
+    /// </summary>
+    /// <param name="bits">
+    /// the bit-length of the scalar to multiply by
+    /// </param>
+    /// <param name="windowSizeCutoffs">
+    /// a monotonically increasing list of bit sizes at which to increment
+    /// the window width
+    /// </param>
+    /// <returns>
+    /// the window size to use
+    /// </returns>
+    class function GetWindowSize(bits: Int32;
+      const windowSizeCutoffs: array of Int32): Int32; overload; static;
+
+    class function MapPointWithPrecomp(const p: IECPoint; width: Int32;
+      includeNegated: Boolean; const pointMap: IECPointMap): IECPoint; static;
+
+    class function Precompute(const p: IECPoint; width: Int32;
+      includeNegated: Boolean): IWNafPreCompInfo; static;
+
+  end;
+
+type
+  TECAlgorithms = class sealed(TObject)
+
+  strict private
+    class function ImplShamirsTrickWNaf(const preCompP,
+      preCompNegP: TCryptoLibGenericArray<IECPoint>;
+      const wnafP: TCryptoLibByteArray;
+      const preCompQ, preCompNegQ: TCryptoLibGenericArray<IECPoint>;
+      const wnafQ: TCryptoLibByteArray): IECPoint; overload; static;
+
+    class function ImplSumOfMultiplies(const negs: TCryptoLibBooleanArray;
+      const infos: TCryptoLibGenericArray<IWNafPreCompInfo>;
+      const wnafs: TCryptoLibMatrixByteArray): IECPoint; overload; static;
+
+  public
+    class function IsF2mCurve(const c: IECCurve): Boolean; static;
+    class function IsF2mField(const field: IFiniteField): Boolean; static;
+    class function IsFpCurve(const c: IECCurve): Boolean; static;
+    class function IsFpField(const field: IFiniteField): Boolean; static;
+
+    class function SumOfMultiplies(const ps: TCryptoLibGenericArray<IECPoint>;
+      const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint; static;
+
+    class function SumOfTwoMultiplies(const p: IECPoint; const a: TBigInteger;
+      const Q: IECPoint; const b: TBigInteger): IECPoint; static;
+
+    // /*
+    // * "Shamir's Trick", originally due to E. G. Straus
+    // * (Addition chains of vectors. American Mathematical Monthly,
+    // * 71(7):806-808, Aug./Sept. 1964)
+    // *
+    // * Input: The points P, Q, scalar k = (km?, ... , k1, k0)
+    // * and scalar l = (lm?, ... , l1, l0).
+    // * Output: R = k * P + l * Q.
+    // * 1: Z <- P + Q
+    // * 2: R <- O
+    // * 3: for i from m-1 down to 0 do
+    // * 4:        R <- R + R        {point doubling}
+    // * 5:        if (ki = 1) and (li = 0) then R <- R + P end if
+    // * 6:        if (ki = 0) and (li = 1) then R <- R + Q end if
+    // * 7:        if (ki = 1) and (li = 1) then R <- R + Z end if
+    // * 8: end for
+    // * 9: return R
+    // */
+    class function ShamirsTrick(const p: IECPoint; const k: TBigInteger;
+      const Q: IECPoint; const l: TBigInteger): IECPoint; static;
+
+    class function ImportPoint(const c: IECCurve; const p: IECPoint)
+      : IECPoint; static;
+
+    class procedure MontgomeryTrick(const zs
+      : TCryptoLibGenericArray<IECFieldElement>; off, len: Int32); overload;
+      static; inline;
+
+    class procedure MontgomeryTrick(const zs
+      : TCryptoLibGenericArray<IECFieldElement>; off, len: Int32;
+      const scale: IECFieldElement); overload; static;
+
+    // /**
+    // * Simple shift-and-add multiplication. Serves as reference implementation
+    // * to verify (possibly faster) implementations, and for very small scalars.
+    // *
+    // * @param p
+    // *            The point to multiply.
+    // * @param k
+    // *            The multiplier.
+    // * @return The result of the point multiplication <code>kP</code>.
+    // */
+    class function ReferenceMultiply(const p: IECPoint; const k: TBigInteger)
+      : IECPoint; static;
+
+    class function ImplCheckResult(const p: IECPoint): IECPoint; static;
+
+    class function ValidatePoint(const p: IECPoint): IECPoint; static;
+
+    class function CleanPoint(const c: IECCurve; const p: IECPoint)
+      : IECPoint; static;
+
+    class function ImplShamirsTrickJsf(const p: IECPoint; const k: TBigInteger;
+      const Q: IECPoint; const l: TBigInteger): IECPoint; static;
+
+    class function ImplShamirsTrickWNaf(const p: IECPoint; const k: TBigInteger;
+      const Q: IECPoint; const l: TBigInteger): IECPoint; overload; static;
+
+    class function ImplShamirsTrickWNaf(const p: IECPoint; const k: TBigInteger;
+      const pointMapQ: IECPointMap; const l: TBigInteger): IECPoint;
+      overload; static;
+
+    class function ImplSumOfMultiplies
+      (const ps: TCryptoLibGenericArray<IECPoint>;
+      const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint;
+      overload; static;
+
+    class function ImplSumOfMultipliesGlv
+      (const ps: TCryptoLibGenericArray<IECPoint>;
+      const ks: TCryptoLibGenericArray<TBigInteger>;
+      const glvEndomorphism: IGlvEndomorphism): IECPoint; static;
+
+    class function ImplSumOfMultiplies
+      (const ps: TCryptoLibGenericArray<IECPoint>; const pointMap: IECPointMap;
+      const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint;
+      overload; static;
+
+  end;
+
+implementation
+
+{ TECAlgorithms }
+
+class function TECAlgorithms.ImplCheckResult(const p: IECPoint): IECPoint;
+begin
+  if (not(p.IsValidPartial())) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidResult);
+  end;
+
+  result := p;
+end;
+
+class function TECAlgorithms.CleanPoint(const c: IECCurve; const p: IECPoint)
+  : IECPoint;
+var
+  cp: IECCurve;
+begin
+  cp := p.Curve;
+  if (not c.Equals(cp)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidPointLocation);
+  end;
+
+  result := c.DecodePoint(p.getEncoded(false));
+end;
+
+class function TECAlgorithms.ValidatePoint(const p: IECPoint): IECPoint;
+begin
+  if (not p.IsValid()) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidPoint);
+  end;
+
+  result := p;
+end;
+
+class function TECAlgorithms.ImplShamirsTrickJsf(const p: IECPoint;
+  const k: TBigInteger; const Q: IECPoint; const l: TBigInteger): IECPoint;
+var
+  Curve: IECCurve;
+  infinity, R: IECPoint;
+  PaddQ, PsubQ: IECPoint;
+  points, table: TCryptoLibGenericArray<IECPoint>;
+  jsf: TCryptoLibByteArray;
+  i, jsfi, kDigit, lDigit, index: Int32;
+begin
+  Curve := p.Curve;
+  infinity := Curve.infinity;
+
+  // TODO conjugate co-Z addition (ZADDC) can return both of these
+  PaddQ := p.Add(Q);
+  PsubQ := p.Subtract(Q);
+
+  points := TCryptoLibGenericArray<IECPoint>.Create(Q, PsubQ, p, PaddQ);
+  Curve.NormalizeAll(points);
+
+  table := TCryptoLibGenericArray<IECPoint>.Create(points[3].Negate(),
+    points[2].Negate(), points[1].Negate(), points[0].Negate(), infinity,
+    points[0], points[1], points[2], points[3]);
+
+  jsf := TWNafUtilities.GenerateJsf(k, l);
+
+  R := infinity;
+
+  i := System.length(jsf);
+  System.Dec(i);
+  while (i >= 0) do
+  begin
+    jsfi := jsf[i];
+
+    // NOTE: The shifting ensures the sign is extended correctly
+    kDigit := (TBits.Asr32((jsfi shl 24), 28));
+    lDigit := (TBits.Asr32((jsfi shl 28), 28));
+
+    index := 4 + (kDigit * 3) + lDigit;
+    R := R.TwicePlus(table[index]);
+    System.Dec(i);
+  end;
+
+  result := R;
+end;
+
+class function TECAlgorithms.ImplShamirsTrickWNaf(const p: IECPoint;
+  const k: TBigInteger; const pointMapQ: IECPointMap; const l: TBigInteger)
+  : IECPoint;
+var
+  negK, negL: Boolean;
+  width: Int32;
+  Q: IECPoint;
+  infoP, infoQ: IWNafPreCompInfo;
+  preCompP, preCompQ, preCompNegP, preCompNegQ
+    : TCryptoLibGenericArray<IECPoint>;
+  wnafP, wnafQ: TCryptoLibByteArray;
+  LK, LL: TBigInteger;
+begin
+  LK := k;
+  LL := l;
+  negK := LK.SignValue < 0;
+  negL := LL.SignValue < 0;
+
+  LK := LK.Abs();
+  LL := LL.Abs();
+
+  width := Max(2, Min(16, TWNafUtilities.GetWindowSize(Max(LK.BitLength,
+    LL.BitLength))));
+
+  Q := TWNafUtilities.MapPointWithPrecomp(p, width, true, pointMapQ);
+  infoP := TWNafUtilities.GetWNafPreCompInfo(p);
+  infoQ := TWNafUtilities.GetWNafPreCompInfo(Q);
+
+  case negK of
+    true:
+      preCompP := infoP.PreCompNeg;
+    false:
+      preCompP := infoP.PreComp;
+  end;
+
+  case negL of
+    true:
+      preCompQ := infoQ.PreCompNeg;
+    false:
+      preCompQ := infoQ.PreComp
+  end;
+
+  case negK of
+    true:
+      preCompNegP := infoP.PreComp;
+    false:
+      preCompNegP := infoP.PreCompNeg;
+  end;
+
+  case negL of
+    true:
+      preCompNegQ := infoQ.PreComp;
+    false:
+      preCompNegQ := infoQ.PreCompNeg
+  end;
+
+  wnafP := TWNafUtilities.GenerateWindowNaf(width, LK);
+  wnafQ := TWNafUtilities.GenerateWindowNaf(width, LL);
+
+  result := ImplShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ,
+    preCompNegQ, wnafQ);
+
+  infoP.PreComp := Nil; // Review
+  infoP.PreCompNeg := Nil; // Review
+  infoQ.PreComp := Nil; // Review
+  infoQ.PreCompNeg := Nil; // Review
+
+end;
+
+class function TECAlgorithms.ImplShamirsTrickWNaf(const p: IECPoint;
+  const k: TBigInteger; const Q: IECPoint; const l: TBigInteger): IECPoint;
+var
+  negK, negL: Boolean;
+  widthP, widthQ: Int32;
+  infoP, infoQ: IWNafPreCompInfo;
+  preCompP, preCompQ, preCompNegP, preCompNegQ
+    : TCryptoLibGenericArray<IECPoint>;
+  wnafP, wnafQ: TCryptoLibByteArray;
+  LK, LL: TBigInteger;
+begin
+  LK := k;
+  LL := l;
+  negK := LK.SignValue < 0;
+  negL := LL.SignValue < 0;
+
+  LK := LK.Abs();
+  LL := LL.Abs();
+
+  widthP := Max(2, Min(16, TWNafUtilities.GetWindowSize(LK.BitLength)));
+  widthQ := Max(2, Min(16, TWNafUtilities.GetWindowSize(LL.BitLength)));
+
+  infoP := TWNafUtilities.Precompute(p, widthP, true);
+  infoQ := TWNafUtilities.Precompute(Q, widthQ, true);
+
+  if negK then
+  begin
+    preCompP := infoP.PreCompNeg
+  end
+  else
+  begin
+    preCompP := infoP.PreComp
+  end;
+
+  if negL then
+  begin
+    preCompQ := infoQ.PreCompNeg
+  end
+  else
+  begin
+    preCompQ := infoQ.PreComp
+  end;
+
+  if negK then
+  begin
+    preCompNegP := infoP.PreComp
+  end
+  else
+  begin
+    preCompNegP := infoP.PreCompNeg
+  end;
+
+  if negL then
+  begin
+    preCompNegQ := infoQ.PreComp
+  end
+  else
+  begin
+    preCompNegQ := infoQ.PreCompNeg
+  end;
+
+  wnafP := TWNafUtilities.GenerateWindowNaf(widthP, LK);
+  wnafQ := TWNafUtilities.GenerateWindowNaf(widthQ, LL);
+
+  result := ImplShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ,
+    preCompNegQ, wnafQ);
+  infoP.PreComp := Nil; // Review
+  infoP.PreCompNeg := Nil; // Review
+  infoQ.PreComp := Nil; // Review
+  infoQ.PreCompNeg := Nil; // Review
+end;
+
+class function TECAlgorithms.ImplShamirsTrickWNaf(const preCompP,
+  preCompNegP: TCryptoLibGenericArray<IECPoint>;
+  const wnafP: TCryptoLibByteArray;
+  const preCompQ, preCompNegQ: TCryptoLibGenericArray<IECPoint>;
+  const wnafQ: TCryptoLibByteArray): IECPoint;
+var
+  len, zeroes, i, wiP, wiQ, nP, nQ: Int32;
+  Curve: IECCurve;
+  infinity, R, point: IECPoint;
+  tableP, tableQ: TCryptoLibGenericArray<IECPoint>;
+begin
+  len := Math.Max(System.length(wnafP), System.length(wnafQ));
+
+  Curve := preCompP[0].Curve;
+  infinity := Curve.infinity;
+
+  R := infinity;
+  zeroes := 0;
+
+  i := len - 1;
+  while (i >= 0) do
+  begin
+
+    if i < System.length(wnafP) then
+    begin
+      wiP := Int32(ShortInt(wnafP[i]));
+    end
+    else
+    begin
+      wiP := 0;
+    end;
+
+    if i < System.length(wnafQ) then
+    begin
+      wiQ := Int32(ShortInt(wnafQ[i]));
+    end
+    else
+    begin
+      wiQ := 0;
+    end;
+
+    if ((wiP or wiQ) = 0) then
+    begin
+      System.Inc(zeroes);
+      System.Dec(i);
+      continue;
+    end;
+
+    point := infinity;
+    if (wiP <> 0) then
+    begin
+      nP := System.Abs(wiP);
+      if wiP < 0 then
+      begin
+        tableP := preCompNegP;
+      end
+      else
+      begin
+        tableP := preCompP;
+      end;
+
+      point := point.Add(tableP[TBits.Asr32(nP, 1)]);
+    end;
+    if (wiQ <> 0) then
+    begin
+
+      nQ := System.Abs(wiQ);
+      if wiQ < 0 then
+      begin
+        tableQ := preCompNegQ;
+      end
+      else
+      begin
+        tableQ := preCompQ;
+      end;
+
+      point := point.Add(tableQ[TBits.Asr32(nQ, 1)]);
+
+    end;
+
+    if (zeroes > 0) then
+    begin
+      R := R.TimesPow2(zeroes);
+      zeroes := 0;
+    end;
+
+    R := R.TwicePlus(point);
+    System.Dec(i);
+  end;
+
+  if (zeroes > 0) then
+  begin
+    R := R.TimesPow2(zeroes);
+  end;
+
+  result := R;
+end;
+
+class function TECAlgorithms.ImplSumOfMultiplies
+  (const ps: TCryptoLibGenericArray<IECPoint>; const pointMap: IECPointMap;
+  const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint;
+var
+  halfCount, fullCount: Int32;
+  negs: TCryptoLibBooleanArray;
+  infos: TCryptoLibGenericArray<IWNafPreCompInfo>;
+  wnafs: TCryptoLibMatrixByteArray;
+  i, j0, j1, width: Int32;
+  kj0, kj1: TBigInteger;
+  p, Q: IECPoint;
+begin
+  halfCount := System.length(ps);
+  fullCount := halfCount shl 1;
+  System.SetLength(negs, fullCount);
+  System.SetLength(infos, fullCount);
+  System.SetLength(wnafs, fullCount);
+
+  for i := 0 to System.Pred(halfCount) do
+  begin
+    j0 := i shl 1;
+    j1 := j0 + 1;
+
+    kj0 := ks[j0];
+    negs[j0] := kj0.SignValue < 0;
+    kj0 := kj0.Abs();
+    kj1 := ks[j1];
+    negs[j1] := kj1.SignValue < 0;
+    kj1 := kj1.Abs();
+
+    width := Max(2, Min(16, TWNafUtilities.GetWindowSize(Max(kj0.BitLength,
+      kj1.BitLength))));
+
+    p := ps[i];
+    Q := TWNafUtilities.MapPointWithPrecomp(p, width, true, pointMap);
+    infos[j0] := TWNafUtilities.GetWNafPreCompInfo(p);
+    infos[j1] := TWNafUtilities.GetWNafPreCompInfo(Q);
+    wnafs[j0] := TWNafUtilities.GenerateWindowNaf(width, kj0);
+    wnafs[j1] := TWNafUtilities.GenerateWindowNaf(width, kj1);
+  end;
+
+  result := ImplSumOfMultiplies(negs, infos, wnafs);
+
+  for i := System.Low(infos) to System.High(infos) do
+  begin
+    infos[i].PreComp := Nil; // Review
+    infos[i].PreCompNeg := Nil; // Review
+  end;
+
+end;
+
+class function TECAlgorithms.ImplSumOfMultiplies
+  (const ps: TCryptoLibGenericArray<IECPoint>;
+  const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint;
+var
+  count, i, width: Int32;
+  negs: TCryptoLibBooleanArray;
+  infos: TCryptoLibGenericArray<IWNafPreCompInfo>;
+  wnafs: TCryptoLibMatrixByteArray;
+  ki: TBigInteger;
+begin
+  count := System.length(ps);
+  System.SetLength(negs, count);
+
+  System.SetLength(infos, count);
+
+  System.SetLength(wnafs, count);
+
+  for i := 0 to System.Pred(count) do
+  begin
+    ki := ks[i];
+    negs[i] := ki.SignValue < 0;
+    ki := ki.Abs();
+
+    width := Max(2, Min(16, TWNafUtilities.GetWindowSize(ki.BitLength)));
+    infos[i] := TWNafUtilities.Precompute(ps[i], width, true);
+    wnafs[i] := TWNafUtilities.GenerateWindowNaf(width, ki);
+  end;
+
+  result := ImplSumOfMultiplies(negs, infos, wnafs);
+
+  for i := System.Low(infos) to System.High(infos) do
+  begin
+    infos[i].PreComp := Nil; // Review
+    infos[i].PreCompNeg := Nil; // Review
+  end;
+
+end;
+
+class function TECAlgorithms.ImplSumOfMultiplies
+  (const negs: TCryptoLibBooleanArray;
+  const infos: TCryptoLibGenericArray<IWNafPreCompInfo>;
+  const wnafs: TCryptoLibMatrixByteArray): IECPoint;
+var
+  len, count, zeroes: Int32;
+  i, J, wi, n: Int32;
+  Curve: IECCurve;
+  infinity, R, point: IECPoint;
+  wnaf: TCryptoLibByteArray;
+  info: IWNafPreCompInfo;
+  table: TCryptoLibGenericArray<IECPoint>;
+begin
+  len := 0;
+  count := System.length(wnafs);
+
+  for i := 0 to System.Pred(count) do
+  begin
+    len := Max(len, System.length(wnafs[i]));
+  end;
+
+  Curve := infos[0].PreComp[0].Curve;
+  infinity := Curve.infinity;
+
+  R := infinity;
+  zeroes := 0;
+
+  i := len - 1;
+  while (i >= 0) do
+  begin
+    point := infinity;
+
+    for J := 0 to System.Pred(count) do
+    begin
+      wnaf := wnafs[J];
+      if i < System.length(wnaf) then
+      begin
+        wi := Int32(ShortInt(wnaf[i]));
+      end
+      else
+      begin
+        wi := 0;
+      end;
+
+      if (wi <> 0) then
+      begin
+        n := System.Abs(wi);
+        info := infos[J];
+        if (wi < 0 = negs[J]) then
+        begin
+          table := info.PreComp;
+        end
+        else
+        begin
+          table := info.PreCompNeg;
+        end;
+
+        point := point.Add(table[TBits.Asr32(n, 1)]);
+      end;
+    end;
+
+    if (point = infinity) then
+    begin
+      System.Inc(zeroes);
+      System.Dec(i);
+      continue;
+    end;
+
+    if (zeroes > 0) then
+    begin
+      R := R.TimesPow2(zeroes);
+      zeroes := 0;
+    end;
+
+    R := R.TwicePlus(point);
+
+    System.Dec(i);
+  end;
+
+  if (zeroes > 0) then
+  begin
+    R := R.TimesPow2(zeroes);
+  end;
+
+  result := R;
+
+end;
+
+class function TECAlgorithms.ImplSumOfMultipliesGlv
+  (const ps: TCryptoLibGenericArray<IECPoint>;
+  const ks: TCryptoLibGenericArray<TBigInteger>;
+  const glvEndomorphism: IGlvEndomorphism): IECPoint;
+var
+  n: TBigInteger;
+  len, i, J: Int32;
+  &abs, ab: TCryptoLibGenericArray<TBigInteger>;
+  pointMap: IECPointMap;
+  pqs: TCryptoLibGenericArray<IECPoint>;
+  p, Q: IECPoint;
+begin
+  n := ps[0].Curve.Order;
+
+  len := System.length(ps);
+
+  System.SetLength(Abs, len shl 1);
+
+  i := 0;
+  J := 0;
+
+  while (i < len) do
+  begin
+    ab := glvEndomorphism.DecomposeScalar(ks[i].&Mod(n));
+
+    Abs[J] := ab[0];
+    System.Inc(J);
+    Abs[J] := ab[1];
+    System.Inc(J);
+    System.Inc(i);
+  end;
+
+  pointMap := glvEndomorphism.pointMap;
+  if (glvEndomorphism.HasEfficientPointMap) then
+  begin
+    result := TECAlgorithms.ImplSumOfMultiplies(ps, pointMap, Abs);
+    Exit;
+  end;
+
+  System.SetLength(pqs, len shl 1);
+
+  i := 0;
+  J := 0;
+
+  while (i < len) do
+  begin
+    p := ps[i];
+    Q := pointMap.Map(p);
+
+    pqs[J] := p;
+    System.Inc(J);
+    pqs[J] := Q;
+    System.Inc(J);
+    System.Inc(i);
+  end;
+
+  result := TECAlgorithms.ImplSumOfMultiplies(pqs, Abs);
+end;
+
+class function TECAlgorithms.ImportPoint(const c: IECCurve; const p: IECPoint)
+  : IECPoint;
+var
+  cp: IECCurve;
+begin
+  cp := p.Curve;
+  if (not c.Equals(cp)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidPointLocation);
+  end;
+
+  result := c.ImportPoint(p);
+end;
+
+class function TECAlgorithms.IsF2mField(const field: IFiniteField): Boolean;
+begin
+  result := (field.Dimension > 1) and
+    (field.Characteristic.Equals(TBigInteger.Two)) and
+    (Supports(field, IPolynomialExtensionField));
+end;
+
+class function TECAlgorithms.IsF2mCurve(const c: IECCurve): Boolean;
+begin
+  result := IsF2mField(c.field);
+end;
+
+class function TECAlgorithms.IsFpField(const field: IFiniteField): Boolean;
+begin
+  result := field.Dimension = 1;
+end;
+
+class function TECAlgorithms.IsFpCurve(const c: IECCurve): Boolean;
+begin
+  result := IsFpField(c.field);
+end;
+
+class procedure TECAlgorithms.MontgomeryTrick
+  (const zs: TCryptoLibGenericArray<IECFieldElement>; off, len: Int32;
+  const scale: IECFieldElement);
+var
+  c: TCryptoLibGenericArray<IECFieldElement>;
+  i, J: Int32;
+  u, tmp: IECFieldElement;
+begin
+  // /*
+  // * Uses the "Montgomery Trick" to invert many field elements, with only a single actual
+  // * field inversion. See e.g. the paper:
+  // * "Fast Multi-scalar Multiplication Methods on Elliptic Curves with Precomputation Strategy Using Montgomery Trick"
+  // * by Katsuyuki Okeya, Kouichi Sakurai.
+  // */
+
+  System.SetLength(c, len);
+
+  c[0] := zs[off];
+
+  i := 0;
+  System.Inc(i);
+  while (i < len) do
+  begin
+    c[i] := c[i - 1].Multiply(zs[off + i]);
+    System.Inc(i);
+  end;
+  System.Dec(i);
+
+  if (scale <> Nil) then
+  begin
+    c[i] := c[i].Multiply(scale);
+  end;
+
+  u := c[i].Invert();
+
+  while (i > 0) do
+  begin
+    J := off + i;
+    System.Dec(i);
+    tmp := zs[J];
+    zs[J] := c[i].Multiply(u);
+    u := u.Multiply(tmp);
+  end;
+
+  zs[off] := u;
+end;
+
+class procedure TECAlgorithms.MontgomeryTrick
+  (const zs: TCryptoLibGenericArray<IECFieldElement>; off, len: Int32);
+begin
+  MontgomeryTrick(zs, off, len, Nil);
+end;
+
+class function TECAlgorithms.ReferenceMultiply(const p: IECPoint;
+  const k: TBigInteger): IECPoint;
+var
+  x: TBigInteger;
+  Q, LP: IECPoint;
+  t, i: Int32;
+begin
+  LP := p;
+  x := k.Abs();
+  Q := LP.Curve.infinity;
+  t := x.BitLength;
+  if (t > 0) then
+  begin
+    if (x.TestBit(0)) then
+    begin
+      Q := LP;
+    end;
+    i := 1;
+    while (i < t) do
+    begin
+      LP := LP.Twice();
+      if (x.TestBit(i)) then
+      begin
+        Q := Q.Add(LP);
+      end;
+      System.Inc(i);
+    end;
+
+  end;
+
+  if k.SignValue < 0 then
+  begin
+    result := Q.Negate();
+  end
+  else
+  begin
+    result := Q;
+  end;
+
+end;
+
+class function TECAlgorithms.ShamirsTrick(const p: IECPoint;
+  const k: TBigInteger; const Q: IECPoint; const l: TBigInteger): IECPoint;
+var
+  cp: IECCurve;
+  LQ: IECPoint;
+begin
+  cp := p.Curve;
+  LQ := Q;
+  LQ := ImportPoint(cp, LQ);
+
+  result := ImplCheckResult(ImplShamirsTrickJsf(p, k, LQ, l));
+end;
+
+class function TECAlgorithms.SumOfMultiplies
+  (const ps: TCryptoLibGenericArray<IECPoint>;
+  const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint;
+var
+  count: Int32;
+  p: IECPoint;
+  c: IECCurve;
+  i: Int32;
+  imported: TCryptoLibGenericArray<IECPoint>;
+  glvEndomorphism: IGlvEndomorphism;
+begin
+  if ((ps = Nil) or (ks = Nil) or (System.length(ps) <> System.length(ks)) or
+    (System.length(ps) < 1)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidArray);
+  end;
+
+  count := System.length(ps);
+
+  case count of
+    1:
+      begin
+        result := ps[0].Multiply(ks[0]);
+        Exit;
+      end;
+
+    2:
+      begin
+        result := SumOfTwoMultiplies(ps[0], ks[0], ps[1], ks[1]);
+        Exit;
+      end;
+
+  end;
+
+  p := ps[0];
+  c := p.Curve;
+  System.SetLength(imported, count);
+  imported[0] := p;
+
+  for i := 1 to System.Pred(count) do
+  begin
+    imported[i] := ImportPoint(c, ps[i]);
+  end;
+
+  if Supports(c.GetEndomorphism(), IGlvEndomorphism, glvEndomorphism) then
+  begin
+    result := ImplCheckResult(ImplSumOfMultipliesGlv(imported, ks,
+      glvEndomorphism));
+    Exit;
+  end;
+
+  result := ImplCheckResult(ImplSumOfMultiplies(imported, ks));
+end;
+
+class function TECAlgorithms.SumOfTwoMultiplies(const p: IECPoint;
+  const a: TBigInteger; const Q: IECPoint; const b: TBigInteger): IECPoint;
+var
+  cp: IECCurve;
+  f2mCurve: IAbstractF2mCurve;
+  glvEndomorphism: IGlvEndomorphism;
+  LQ: IECPoint;
+begin
+  cp := p.Curve;
+  LQ := Q;
+  LQ := ImportPoint(cp, LQ);
+
+  // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick
+
+  if (Supports(cp, IAbstractF2mCurve, f2mCurve) and (f2mCurve.IsKoblitz)) then
+  begin
+    result := ImplCheckResult(p.Multiply(a).Add(LQ.Multiply(b)));
+    Exit;
+  end;
+
+  if Supports(cp.GetEndomorphism(), IGlvEndomorphism, glvEndomorphism) then
+  begin
+    result := ImplCheckResult
+      (ImplSumOfMultipliesGlv(TCryptoLibGenericArray<IECPoint>.Create(p, LQ),
+      TCryptoLibGenericArray<TBigInteger>.Create(a, b), glvEndomorphism));
+    Exit;
+  end;
+
+  result := ImplCheckResult(ImplShamirsTrickWNaf(p, a, LQ, b));
+end;
+
+{ TWNafUtilities }
+
+class function TWNafUtilities.ResizeTable
+  (const a: TCryptoLibGenericArray<IECPoint>; length: Int32)
+  : TCryptoLibGenericArray<IECPoint>;
+begin
+  result := System.Copy(a);
+  System.SetLength(result, length);
+end;
+
+class function TWNafUtilities.Trim(const a: TCryptoLibInt32Array; length: Int32)
+  : TCryptoLibInt32Array;
+begin
+  result := System.Copy(a, 0, length);
+end;
+
+class function TWNafUtilities.Trim(const a: TCryptoLibByteArray; length: Int32)
+  : TCryptoLibByteArray;
+begin
+  result := System.Copy(a, 0, length);
+end;
+
+class function TWNafUtilities.CheckTable(const table
+  : TCryptoLibGenericArray<IECPoint>; reqLen: Int32): Boolean;
+begin
+  result := (table <> Nil) and (System.length(table) >= reqLen);
+end;
+
+class procedure TWNafUtilities.Boot;
+begin
+  FEMPTY_BYTES := Nil;
+  FEMPTY_INTS := Nil;
+end;
+
+class constructor TWNafUtilities.CreateWNafUtilities;
+begin
+  TWNafUtilities.Boot;
+end;
+
+class function TWNafUtilities.CheckExisting(const existingWNaf
+  : IWNafPreCompInfo; reqPreCompLen: Int32; includeNegated: Boolean): Boolean;
+begin
+  result := (existingWNaf <> Nil) and CheckTable(existingWNaf.PreComp,
+    reqPreCompLen) and ((not includeNegated) or
+    CheckTable(existingWNaf.PreCompNeg, reqPreCompLen));
+end;
+
+class function TWNafUtilities.GenerateCompactNaf(const k: TBigInteger)
+  : TCryptoLibInt32Array;
+var
+  _3k, diff: TBigInteger;
+  bits, highBit, &length, zeroes, i, digit: Int32;
+  naf: TCryptoLibInt32Array;
+begin
+  if ((TBits.Asr32(k.BitLength, 16)) <> 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidBitLength);
+  end;
+  if (k.SignValue = 0) then
+  begin
+    result := FEMPTY_INTS;
+    Exit;
+  end;
+
+  _3k := k.ShiftLeft(1).Add(k);
+
+  bits := _3k.BitLength;
+  System.SetLength(naf, TBits.Asr32(bits, 1));
+
+  diff := _3k.&Xor(k);
+
+  highBit := bits - 1;
+  &length := 0;
+  zeroes := 0;
+
+  i := 1;
+
+  while (i < highBit) do
+  begin
+    if (not diff.TestBit(i)) then
+    begin
+      System.Inc(zeroes);
+      System.Inc(i);
+      continue;
+    end;
+
+    if k.TestBit(i) then
+    begin
+      digit := -1;
+    end
+    else
+    begin
+      digit := 1;
+    end;
+
+    naf[length] := (digit shl 16) or zeroes;
+    System.Inc(length);
+    zeroes := 1;
+
+    System.Inc(i, 2);
+
+  end;
+
+  naf[length] := (1 shl 16) or zeroes;
+  System.Inc(length);
+
+  if (System.length(naf) > length) then
+  begin
+    naf := Trim(naf, length);
+  end;
+
+  result := naf;
+end;
+
+class function TWNafUtilities.GenerateCompactWindowNaf(width: Int32;
+  const k: TBigInteger): TCryptoLibInt32Array;
+var
+  wnaf: TCryptoLibInt32Array;
+  pow2, mask, sign, &length, &pos, digit, zeroes: Int32;
+  carry: Boolean;
+  LK: TBigInteger;
+begin
+  LK := k;
+  if (width = 2) then
+  begin
+    result := GenerateCompactNaf(LK);
+    Exit;
+  end;
+
+  if ((width < 2) or (width > 16)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidRange);
+  end;
+  if ((TBits.Asr32(LK.BitLength, 16)) <> 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidBitLength);
+  end;
+  if (LK.SignValue = 0) then
+  begin
+    result := FEMPTY_INTS;
+    Exit;
+  end;
+
+  System.SetLength(wnaf, (LK.BitLength div width) + 1);
+
+  // 2^width and a mask and sign bit set accordingly
+  pow2 := 1 shl width;
+  mask := pow2 - 1;
+  sign := TBits.Asr32(pow2, 1);
+
+  carry := false;
+  length := 0;
+  pos := 0;
+
+  while (pos <= LK.BitLength) do
+  begin
+    if (LK.TestBit(pos) = carry) then
+    begin
+      System.Inc(pos);
+      continue;
+    end;
+
+    LK := LK.ShiftRight(pos);
+
+    digit := LK.Int32Value and mask;
+    if (carry) then
+    begin
+      System.Inc(digit);
+    end;
+
+    carry := (digit and sign) <> 0;
+    if (carry) then
+    begin
+      digit := digit - pow2;
+    end;
+
+    if length > 0 then
+    begin
+      zeroes := pos - 1;
+    end
+    else
+    begin
+      zeroes := pos;
+    end;
+
+    wnaf[length] := (digit shl 16) or zeroes;
+    System.Inc(length);
+    pos := width;
+  end;
+
+  // Reduce the WNAF array to its actual length
+  if (System.length(wnaf) > length) then
+  begin
+    wnaf := Trim(wnaf, length);
+  end;
+
+  result := wnaf;
+end;
+
+class function TWNafUtilities.GenerateJsf(const g, h: TBigInteger)
+  : TCryptoLibByteArray;
+var
+  digits, J, d0, d1, offset, n0, n1, u0, u1: Int32;
+  jsf: TCryptoLibByteArray;
+  k0, k1: TBigInteger;
+begin
+  digits := Max(g.BitLength, h.BitLength) + 1;
+
+  System.SetLength(jsf, digits);
+
+  k0 := g;
+  k1 := h;
+  J := 0;
+  d0 := 0;
+  d1 := 0;
+
+  offset := 0;
+
+  while (((d0 or d1) <> 0) or (k0.BitLength > offset) or
+    (k1.BitLength > offset)) do
+  begin
+    n0 := (Int32(UInt32(k0.Int32Value) shr offset) + d0) and 7;
+    n1 := (Int32(UInt32(k1.Int32Value) shr offset) + d1) and 7;
+
+    u0 := n0 and 1;
+    if (u0 <> 0) then
+    begin
+      u0 := u0 - (n0 and 2);
+      if (((n0 + u0) = 4) and ((n1 and 3) = 2)) then
+      begin
+        u0 := -u0;
+      end;
+    end;
+
+    u1 := n1 and 1;
+    if (u1 <> 0) then
+    begin
+      u1 := u1 - (n1 and 2);
+      if (((n1 + u1) = 4) and ((n0 and 3) = 2)) then
+      begin
+        u1 := -u1;
+      end;
+    end;
+
+    if ((d0 shl 1) = (1 + u0)) then
+    begin
+      d0 := d0 xor 1;
+    end;
+    if ((d1 shl 1) = (1 + u1)) then
+    begin
+      d1 := d1 xor 1;
+    end;
+
+    System.Inc(offset);
+    if (offset = 30) then
+    begin
+      offset := 0;
+      k0 := k0.ShiftRight(30);
+      k1 := k1.ShiftRight(30);
+    end;
+
+    jsf[J] := Byte((u0 shl 4) or (u1 and $F));
+    System.Inc(J);
+  end;
+
+  // Reduce the JSF array to its actual length
+  if (System.length(jsf) > J) then
+  begin
+    jsf := Trim(jsf, J);
+  end;
+
+  result := jsf;
+end;
+
+class function TWNafUtilities.GenerateNaf(const k: TBigInteger)
+  : TCryptoLibByteArray;
+var
+  _3k, diff: TBigInteger;
+  digits, i: Int32;
+  naf: TCryptoLibByteArray;
+begin
+  if (k.SignValue = 0) then
+  begin
+    result := FEMPTY_BYTES;
+    Exit;
+  end;
+
+  _3k := k.ShiftLeft(1).Add(k);
+
+  digits := _3k.BitLength - 1;
+  System.SetLength(naf, digits);
+
+  diff := _3k.&Xor(k);
+
+  i := 1;
+
+  while i < digits do
+  begin
+    if (diff.TestBit(i)) then
+    begin
+      if k.TestBit(i) then
+      begin
+        naf[i - 1] := Byte(-1);
+      end
+      else
+      begin
+        naf[i - 1] := Byte(1);
+      end;
+
+      System.Inc(i);
+    end;
+    System.Inc(i);
+  end;
+
+  naf[digits - 1] := 1;
+
+  result := naf;
+end;
+
+class function TWNafUtilities.GenerateWindowNaf(width: Int32;
+  const k: TBigInteger): TCryptoLibByteArray;
+var
+  wnaf: TCryptoLibByteArray;
+  pow2, mask, sign, &length, &pos, digit: Int32;
+  carry: Boolean;
+  LK: TBigInteger;
+begin
+  LK := k;
+  if (width = 2) then
+  begin
+    result := GenerateNaf(LK);
+    Exit;
+  end;
+
+  if ((width < 2) or (width > 8)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidRange2);
+  end;
+  if (LK.SignValue = 0) then
+  begin
+    result := FEMPTY_BYTES;
+    Exit;
+  end;
+
+  System.SetLength(wnaf, LK.BitLength + 1);
+
+  // 2^width and a mask and sign bit set accordingly
+  pow2 := 1 shl width;
+  mask := pow2 - 1;
+  sign := TBits.Asr32(pow2, 1);
+
+  carry := false;
+  length := 0;
+  pos := 0;
+
+  while (pos <= LK.BitLength) do
+  begin
+    if (LK.TestBit(pos) = carry) then
+    begin
+      System.Inc(pos);
+      continue;
+    end;
+
+    LK := LK.ShiftRight(pos);
+
+    digit := LK.Int32Value and mask;
+    if (carry) then
+    begin
+      System.Inc(digit);
+    end;
+
+    carry := (digit and sign) <> 0;
+    if (carry) then
+    begin
+      digit := digit - pow2;
+    end;
+
+    if length > 0 then
+    begin
+      length := length + (pos - 1);
+    end
+    else
+    begin
+      length := length + (pos);
+    end;
+
+    wnaf[length] := Byte(digit);
+    System.Inc(length);
+    pos := width;
+  end;
+
+  // Reduce the WNAF array to its actual length
+  if (System.length(wnaf) > length) then
+  begin
+    wnaf := Trim(wnaf, length);
+  end;
+
+  result := wnaf;
+end;
+
+class function TWNafUtilities.GetNafWeight(const k: TBigInteger): Int32;
+var
+  _3k, diff: TBigInteger;
+begin
+  if (k.SignValue = 0) then
+  begin
+    result := 0;
+    Exit;
+  end;
+
+  _3k := k.ShiftLeft(1).Add(k);
+  diff := _3k.&Xor(k);
+
+  result := diff.BitCount;
+end;
+
+class function TWNafUtilities.GetWindowSize(bits: Int32;
+  const windowSizeCutoffs: array of Int32): Int32;
+var
+  w: Int32;
+begin
+  w := 0;
+  while (w < System.length(windowSizeCutoffs)) do
+  begin
+    if (bits < windowSizeCutoffs[w]) then
+    begin
+      break;
+    end;
+    System.Inc(w);
+  end;
+
+  result := w + 2;
+end;
+
+class function TWNafUtilities.GetWindowSize(bits: Int32): Int32;
+begin
+  result := GetWindowSize(bits, FDEFAULT_WINDOW_SIZE_CUTOFFS);
+end;
+
+class function TWNafUtilities.GetWNafPreCompInfo(const preCompInfo
+  : IPreCompInfo): IWNafPreCompInfo;
+begin
+  result := preCompInfo as IWNafPreCompInfo;
+end;
+
+class function TWNafUtilities.GetWNafPreCompInfo(const p: IECPoint)
+  : IWNafPreCompInfo;
+var
+  preCompInfo: IPreCompInfo;
+begin
+  preCompInfo := p.Curve.GetPreCompInfo(p, PRECOMP_NAME);
+  result := GetWNafPreCompInfo(preCompInfo);
+end;
+
+class function TWNafUtilities.MapPointWithPrecomp(const p: IECPoint;
+  width: Int32; includeNegated: Boolean; const pointMap: IECPointMap): IECPoint;
+var
+  c: IECCurve;
+  wnafPreCompP: IWNafPreCompInfo;
+  Q: IECPoint;
+begin
+  c := p.Curve;
+
+  wnafPreCompP := Precompute(p, width, includeNegated);
+
+  Q := pointMap.Map(p);
+
+  c.Precompute(Q, PRECOMP_NAME, TMapPointCallback.Create(wnafPreCompP,
+    includeNegated, pointMap) as IMapPointCallback);
+
+  result := Q;
+
+end;
+
+class function TWNafUtilities.Precompute(const p: IECPoint; width: Int32;
+  includeNegated: Boolean): IWNafPreCompInfo;
+begin
+  result := p.Curve.Precompute(p, PRECOMP_NAME, TWNafCallback.Create(p, width,
+    includeNegated) as IWNafCallback) as IWNafPreCompInfo;
+end;
+
+{ TWNafUtilities.TMapPointCallback }
+
+constructor TWNafUtilities.TMapPointCallback.Create(const wnafPreCompP
+  : IWNafPreCompInfo; includeNegated: Boolean; const pointMap: IECPointMap);
+begin
+  Inherited Create();
+  Fm_wnafPreCompP := wnafPreCompP;
+  Fm_includeNegated := includeNegated;
+  Fm_pointMap := pointMap;
+end;
+
+function TWNafUtilities.TMapPointCallback.Precompute(const existing
+  : IPreCompInfo): IPreCompInfo;
+var
+  tempResult: IWNafPreCompInfo;
+  twiceP, twiceQ: IECPoint;
+  preCompP, preCompQ, preCompNegQ: TCryptoLibGenericArray<IECPoint>;
+  i: Int32;
+begin
+  tempResult := TWNafPreCompInfo.Create();
+
+  twiceP := Fm_wnafPreCompP.Twice;
+  if (twiceP <> Nil) then
+  begin
+    twiceQ := Fm_pointMap.Map(twiceP);
+    tempResult.Twice := twiceQ;
+  end;
+
+  preCompP := Fm_wnafPreCompP.PreComp;
+
+  System.SetLength(preCompQ, System.length(preCompP));
+  for i := 0 to System.Pred(System.length(preCompP)) do
+  begin
+    preCompQ[i] := Fm_pointMap.Map(preCompP[i]);
+  end;
+
+  tempResult.PreComp := preCompQ;
+
+  if (Fm_includeNegated) then
+  begin
+
+    System.SetLength(preCompNegQ, System.length(preCompQ));
+
+    for i := 0 to System.Pred(System.length(preCompNegQ)) do
+    begin
+      preCompNegQ[i] := preCompQ[i].Negate();
+    end;
+
+    tempResult.PreCompNeg := preCompNegQ;
+  end;
+
+  result := tempResult;
+end;
+
+{ TWNafUtilities.TWNafCallback }
+
+constructor TWNafUtilities.TWNafCallback.Create(const p: IECPoint; width: Int32;
+  includeNegated: Boolean);
+begin
+  Inherited Create();
+  Fm_p := p;
+  Fm_width := width;
+  Fm_includeNegated := includeNegated;
+end;
+
+function TWNafUtilities.TWNafCallback.Precompute(const existing: IPreCompInfo)
+  : IPreCompInfo;
+var
+  twiceP, isoTwiceP, last: IECPoint;
+  c: IECCurve;
+  PreComp, PreCompNeg, EMPTY_POINTS: TCryptoLibGenericArray<IECPoint>;
+  tempRes, existingWNaf: IWNafPreCompInfo;
+  reqPreCompLen, iniPreCompLen, curPreCompLen, pos: Int32;
+  iso, iso2, iso3: IECFieldElement;
+begin
+  EMPTY_POINTS := Nil;
+  existingWNaf := existing as IWNafPreCompInfo;
+
+  reqPreCompLen := 1 shl Max(0, Fm_width - 2);
+
+  if (CheckExisting(existingWNaf, reqPreCompLen, Fm_includeNegated)) then
+  begin
+    result := existingWNaf;
+    Exit;
+  end;
+
+  c := Fm_p.Curve;
+
+  if (existingWNaf <> Nil) then
+  begin
+    PreComp := existingWNaf.PreComp;
+    PreCompNeg := existingWNaf.PreCompNeg;
+    twiceP := existingWNaf.Twice;
+  end;
+
+  iniPreCompLen := 0;
+  if (PreComp = Nil) then
+  begin
+    PreComp := EMPTY_POINTS;
+  end
+  else
+  begin
+    iniPreCompLen := System.length(PreComp);
+  end;
+
+  if (iniPreCompLen < reqPreCompLen) then
+  begin
+    PreComp := TWNafUtilities.ResizeTable(PreComp, reqPreCompLen);
+
+    if (reqPreCompLen = 1) then
+    begin
+      PreComp[0] := Fm_p.Normalize();
+    end
+    else
+    begin
+      curPreCompLen := iniPreCompLen;
+      if (curPreCompLen = 0) then
+      begin
+        PreComp[0] := Fm_p;
+        curPreCompLen := 1;
+      end;
+
+      if (reqPreCompLen = 2) then
+      begin
+        PreComp[1] := Fm_p.threeTimes();
+      end
+      else
+      begin
+        isoTwiceP := twiceP;
+        last := PreComp[curPreCompLen - 1];
+        if (isoTwiceP = Nil) then
+        begin
+          isoTwiceP := PreComp[0].Twice();
+          twiceP := isoTwiceP;
+          //
+          // /*
+          // * For Fp curves with Jacobian projective coordinates, use a (quasi-)isomorphism
+          // * where 'twiceP' is "affine", so that the subsequent additions are cheaper. This
+          // * also requires scaling the initial point's X, Y coordinates, and reversing the
+          // * isomorphism as part of the subsequent normalization.
+          // *
+          // *  NOTE: The correctness of this optimization depends on:
+          // *      1) additions do not use the curve's A, B coefficients.
+          // *      2) no special cases (i.e. Q +/- Q) when calculating 1P, 3P, 5P, ...
+          // */
+          if ((not(twiceP.IsInfinity)) and (TECAlgorithms.IsFpCurve(c)) and
+            (c.FieldSize >= 64)) then
+          begin
+            case (c.CoordinateSystem) of
+              TECCurveConstants.COORD_JACOBIAN,
+                TECCurveConstants.COORD_JACOBIAN_CHUDNOVSKY,
+                TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+
+                begin
+                  iso := twiceP.GetZCoord(0);
+                  isoTwiceP := c.CreatePoint(twiceP.XCoord.ToBigInteger,
+                    twiceP.YCoord.ToBigInteger());
+
+                  iso2 := iso.square();
+                  iso3 := iso2.Multiply(iso);
+                  last := last.scaleX(iso2).scaleY(iso3);
+
+                  if (iniPreCompLen = 0) then
+                  begin
+                    PreComp[0] := last;
+                  end;
+                end;
+
+            end;
+
+          end;
+        end;
+
+        while (curPreCompLen < reqPreCompLen) do
+        begin
+          // /*
+          // * Compute the new ECPoints for the precomputation array. The values 1, 3,
+          // * 5, ..., 2^(width-1)-1 times p are computed
+          // */
+          last := last.Add(isoTwiceP);
+          PreComp[curPreCompLen] := last;
+          System.Inc(curPreCompLen);
+        end;
+      end;
+      //
+      // /*
+      // * Having oft-used operands in affine form makes operations faster.
+      // */
+      c.NormalizeAll(PreComp, iniPreCompLen,
+        reqPreCompLen - iniPreCompLen, iso);
+    end;
+  end;
+
+  if (Fm_includeNegated) then
+  begin
+
+    if (PreCompNeg = Nil) then
+    begin
+      pos := 0;
+      System.SetLength(PreCompNeg, reqPreCompLen);
+
+    end
+    else
+    begin
+      pos := System.length(PreCompNeg);
+      if (pos < reqPreCompLen) then
+      begin
+        PreCompNeg := TWNafUtilities.ResizeTable(PreCompNeg, reqPreCompLen);
+      end;
+    end;
+
+    while (pos < reqPreCompLen) do
+    begin
+      PreCompNeg[pos] := PreComp[pos].Negate();
+      System.Inc(pos);
+    end;
+  end;
+
+  tempRes := TWNafPreCompInfo.Create();
+  tempRes.PreComp := PreComp;
+  tempRes.PreCompNeg := PreCompNeg;
+  tempRes.Twice := twiceP;
+
+  result := tempRes;
+end;
+
+end.

+ 6505 - 0
src/libraries/cryptolib4pascal/ClpECC.pas

@@ -0,0 +1,6505 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECC;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  SyncObjs,
+  SysUtils,
+  Generics.Collections,
+  ClpNat,
+  ClpMod,
+  ClpArrayUtils,
+  ClpIPreCompCallback,
+  ClpCryptoLibTypes,
+  ClpBigInteger,
+  ClpBigIntegers,
+  ClpBits,
+  ClpIGlvEndomorphism,
+  ClpECAlgorithms,
+  ClpLongArray,
+  ClpGlvMultiplier,
+  ClpWNafL2RMultiplier,
+  ClpWTauNafMultiplier,
+  ClpFiniteFields,
+  ClpSetWeakRef,
+  ClpECCurveConstants,
+  ClpTnaf,
+  ClpValidityPrecompInfo,
+  ClpIValidityPrecompInfo,
+  ClpIECC,
+  ClpIFiniteField,
+  ClpIPreCompInfo;
+
+resourcestring
+  SInvalidValue = 'Value Invalid in Fp Field Element, " x "';
+  SInvalidValue2 = 'Value Invalid in F2m Field Element, "x"';
+  SInvalidK2Value = 'k2 must be smaller than k3';
+  SInvalidK2Value2 = 'k2 must be larger than 0';
+  SInvalidFieldElement =
+    'Field elements are not both instances of F2mFieldElement';
+  SInvalidFieldElements =
+    'Field elements are not elements of the same field F2m';
+  SIncorrectRepresentation =
+    'One of the F2m field elements has incorrect representation';
+  SEvenValue = 'Even Value of Q';
+  STraceInternalErrorCalculation = 'Internal Error in Trace Calculation';
+  SHalfTraceUndefinedForM = 'Half-Trace Only Defined For Odd M';
+  SUnSupportedCoordinateSystem = 'UnSupported Coordinate System';
+  SCurrentCurve = 'Implementation returned Current Curve';
+  SInvalidPointCoordinates = 'Invalid Point Coordinates';
+  SInvalidAffineCoordinates = 'not valid for affine coordinates, "iso"';
+  SInvalidPointOnCurve = 'must be non-null and on this curve, "point"';
+  SInvalidPointOnCurve2 = 'Entries must be null or on this curve, "points"';
+  SPointsNil = 'points';
+  SInvalidRangeSpecified = 'Invalid Range Specified", "points"';
+  SInvalidPointCompression = 'Invalid Point Compression';
+  SInvalidK1 = 'k1 must be > 0';
+  SInvalidK3 = 'k3 must be 0 if k2 == 0';
+  SK2K1MisMatch = 'k2 must be > k1';
+  SK3K2Mismatch = 'k3 must be > k2';
+  SInvalidInfinityEncoding = 'Invalid Infinity Encoding, "encoded"';
+  SInvalidPointEncoding = 'Invalid Point Encoding %u';
+  SIncorrectLengthInfinityEncoding =
+    'Incorrect Length for infinity encoding", "encoded"';
+  SIncorrectLengthCompressedEncoding =
+    'Incorrect Length for Compressed Encoding", "encoded"';
+  SInvalidPoint = 'Invalid Point';
+  SIncorrectLengthUnCompressedEncoding =
+    'Incorrect Length for UnCompressed Encoding", "encoded"';
+  SIncorrectLengthHybridEncoding =
+    'Incorrect Length for Hybrid Encoding", "encoded"';
+  SInConsistentYCoord =
+    'Inconsistent Y Coordinate in Hybrid Encoding", "encoded"';
+  SUnknownCoordSystem = 'Unknown Coordinate System';
+  SPointNotInNormalForm = 'Point not in Normal Form';
+  SNotProjectiveCoordSystem = 'Not a Projective Coordinate System';
+  SCannotBeNegative = 'Cannot be Negative, "e"';
+  SNilFieldElement = 'Exactly one of the Field Elements is Nil';
+
+type
+  TECFieldElement = class abstract(TInterfacedObject, IECFieldElement)
+
+  strict protected
+
+    function GetBitLength: Int32; virtual;
+    function GetIsOne: Boolean; virtual;
+    function GetIsZero: Boolean; virtual;
+
+    function GetFieldName: String; virtual; abstract;
+    function GetFieldSize: Int32; virtual; abstract;
+
+  public
+
+    constructor Create();
+    destructor Destroy; override;
+
+    function ToBigInteger(): TBigInteger; virtual; abstract;
+    function Add(const b: IECFieldElement): IECFieldElement; virtual; abstract;
+    function AddOne(): IECFieldElement; virtual; abstract;
+    function Subtract(const b: IECFieldElement): IECFieldElement;
+      virtual; abstract;
+    function Multiply(const b: IECFieldElement): IECFieldElement;
+      virtual; abstract;
+    function Divide(const b: IECFieldElement): IECFieldElement;
+      virtual; abstract;
+    function Negate(): IECFieldElement; virtual; abstract;
+    function Square(): IECFieldElement; virtual; abstract;
+    function Invert(): IECFieldElement; virtual; abstract;
+    function Sqrt(): IECFieldElement; virtual; abstract;
+
+    function MultiplyMinusProduct(const b, x, y: IECFieldElement)
+      : IECFieldElement; virtual;
+
+    function MultiplyPlusProduct(const b, x, y: IECFieldElement)
+      : IECFieldElement; virtual;
+
+    function SquareMinusProduct(const x, y: IECFieldElement)
+      : IECFieldElement; virtual;
+
+    function SquarePlusProduct(const x, y: IECFieldElement)
+      : IECFieldElement; virtual;
+
+    function SquarePow(pow: Int32): IECFieldElement; virtual;
+
+    function TestBitZero(): Boolean; virtual;
+
+    function Equals(const other: IECFieldElement): Boolean;
+      reintroduce; virtual;
+
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    function ToString(): String; override;
+
+    function GetEncoded(): TCryptoLibByteArray; virtual;
+
+    property FieldName: string read GetFieldName;
+    property FieldSize: Int32 read GetFieldSize;
+    property BitLength: Int32 read GetBitLength;
+    property IsOne: Boolean read GetIsOne;
+    property IsZero: Boolean read GetIsZero;
+
+  end;
+
+type
+  TAbstractFpFieldElement = class abstract(TECFieldElement,
+    IAbstractFpFieldElement)
+
+  end;
+
+type
+  TFpFieldElement = class(TAbstractFpFieldElement, IFpFieldElement)
+
+  strict private
+    Fq, Fr, Fx: TBigInteger;
+
+    function GetQ: TBigInteger; inline;
+
+    function CheckSqrt(const z: IECFieldElement): IECFieldElement; inline;
+    function LucasSequence(const P, Q, K: TBigInteger)
+      : TCryptoLibGenericArray<TBigInteger>;
+
+  strict protected
+    function ModAdd(const x1, x2: TBigInteger): TBigInteger; virtual;
+    function ModDouble(const x: TBigInteger): TBigInteger; virtual;
+    function ModHalf(const x: TBigInteger): TBigInteger; virtual;
+    function ModHalfAbs(const x: TBigInteger): TBigInteger; virtual;
+    function ModInverse(const x: TBigInteger): TBigInteger; virtual;
+    function ModMult(const x1, x2: TBigInteger): TBigInteger; virtual;
+    function ModReduce(const x: TBigInteger): TBigInteger; virtual;
+    function ModSubtract(const x1, x2: TBigInteger): TBigInteger; virtual;
+
+    /// <summary>
+    /// return the field name for this field.
+    /// </summary>
+    /// <returns>
+    /// return the string "Fp".
+    /// </returns>
+    function GetFieldName: String; override;
+    function GetFieldSize: Int32; override;
+
+  public
+    constructor Create(const Q, x: TBigInteger); overload;
+      deprecated 'Use ECCurve.FromBigInteger to construct field elements';
+
+    constructor Create(const Q, r, x: TBigInteger); overload;
+
+    destructor Destroy; override;
+
+    function ToBigInteger(): TBigInteger; override;
+
+    function Add(const b: IECFieldElement): IECFieldElement; override;
+    function AddOne(): IECFieldElement; override;
+    function Subtract(const b: IECFieldElement): IECFieldElement; override;
+
+    function Multiply(const b: IECFieldElement): IECFieldElement; override;
+    function Divide(const b: IECFieldElement): IECFieldElement; override;
+    function Negate(): IECFieldElement; override;
+    function Square(): IECFieldElement; override;
+
+    function Invert(): IECFieldElement; override;
+
+    /// <summary>
+    /// return a sqrt root - the routine verifies that the calculation
+    /// </summary>
+    /// <returns>
+    /// returns the right value - if none exists it returns null.
+    /// </returns>
+    function Sqrt(): IECFieldElement; override;
+
+    function MultiplyMinusProduct(const b, x, y: IECFieldElement)
+      : IECFieldElement; override;
+    function MultiplyPlusProduct(const b, x, y: IECFieldElement)
+      : IECFieldElement; override;
+
+    function SquareMinusProduct(const x, y: IECFieldElement)
+      : IECFieldElement; override;
+
+    function SquarePlusProduct(const x, y: IECFieldElement)
+      : IECFieldElement; override;
+
+    property FieldName: string read GetFieldName;
+    property FieldSize: Int32 read GetFieldSize;
+
+    property Q: TBigInteger read GetQ;
+
+    function Equals(const other: IFpFieldElement): Boolean; reintroduce; overload;
+
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    class function CalculateResidue(const P: TBigInteger): TBigInteger; static;
+
+  end;
+
+type
+  TAbstractF2mFieldElement = class abstract(TECFieldElement,
+    IAbstractF2mFieldElement)
+
+  public
+    function Trace(): Int32; virtual;
+    function HalfTrace(): IECFieldElement; virtual;
+
+  end;
+
+type
+  /// **
+  // * Class representing the Elements of the finite field
+  // * <code>F<sub>2<sup>m</sup></sub></code> in polynomial basis (PB)
+  // * representation. Both trinomial (Tpb) and pentanomial (Ppb) polynomial
+  // * basis representations are supported. Gaussian normal basis (GNB)
+  // * representation is not supported.
+  // */
+  TF2mFieldElement = class(TAbstractF2mFieldElement, IF2mFieldElement)
+
+  strict private
+
+  var
+    Frepresentation, Fm: Int32;
+    FKs: TCryptoLibInt32Array;
+    Fx: TLongArray;
+    // /**
+    // * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>.
+    // */
+    function GetM: Int32; inline;
+    /// <summary>
+    /// Tpb or Ppb.
+    /// </summary>
+    function GetRepresentation: Int32; inline;
+    function GetKs: TCryptoLibInt32Array; inline;
+    function GetX: TLongArray; inline;
+
+    function GetK1: Int32; inline;
+    function GetK2: Int32; inline;
+    function GetK3: Int32; inline;
+
+  strict protected
+
+    function GetBitLength: Int32; override;
+    function GetIsOne: Boolean; override;
+    function GetIsZero: Boolean; override;
+
+    function GetFieldName: String; override;
+    function GetFieldSize: Int32; override;
+
+  public
+
+    const
+
+    /// <summary>
+    /// Indicates gaussian normal basis representation (GNB). Number
+    /// chosen according to X9.62. GNB is not implemented at present. <br />
+    /// </summary>
+    Gnb = Int32(1);
+
+    /// <summary>
+    /// Indicates trinomial basis representation (Tpb). Number chosen
+    /// according to X9.62. <br />
+    /// </summary>
+    Tpb = Int32(2);
+
+    /// <summary>
+    /// Indicates pentanomial basis representation (Ppb). Number chosen
+    /// according to X9.62. <br />
+    /// </summary>
+    Ppb = Int32(3);
+
+    // /**
+    // * Constructor for Ppb.
+    // * @param m  The exponent <code>m</code> of
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.
+    // * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.
+    // * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.
+    // * @param x The BigInteger representing the value of the field element.
+    // */
+    constructor Create(m, k1, k2, k3: Int32; const x: TBigInteger); overload;
+      deprecated 'Use ECCurve.FromBigInteger to construct field elements';
+    // /**
+    // * Constructor for Tpb.
+    // * @param m  The exponent <code>m</code> of
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // * @param k The integer <code>k</code> where <code>x<sup>m</sup> +
+    // * x<sup>k</sup> + 1</code> represents the reduction
+    // * polynomial <code>f(z)</code>.
+    // * @param x The BigInteger representing the value of the field element.
+    // */
+    constructor Create(m, K: Int32; const x: TBigInteger); overload;
+      deprecated 'Use ECCurve.FromBigInteger to construct field elements';
+
+    constructor Create(m: Int32; const ks: TCryptoLibInt32Array;
+      const x: TLongArray); overload;
+
+    destructor Destroy; override;
+
+    function TestBitZero(): Boolean; override;
+    function ToBigInteger(): TBigInteger; override;
+
+    function Add(const b: IECFieldElement): IECFieldElement; override;
+    function AddOne(): IECFieldElement; override;
+    function Subtract(const b: IECFieldElement): IECFieldElement; override;
+
+    function Multiply(const b: IECFieldElement): IECFieldElement; override;
+    function Divide(const b: IECFieldElement): IECFieldElement; override;
+    function Negate(): IECFieldElement; override;
+    function Square(): IECFieldElement; override;
+
+    function Invert(): IECFieldElement; override;
+
+    /// <summary>
+    /// return a sqrt root - the routine verifies that the calculation
+    /// </summary>
+    /// <returns>
+    /// returns the right value - if none exists it returns null.
+    /// </returns>
+    function Sqrt(): IECFieldElement; override;
+
+    function MultiplyMinusProduct(const b, x, y: IECFieldElement)
+      : IECFieldElement; override;
+    function MultiplyPlusProduct(const b, x, y: IECFieldElement)
+      : IECFieldElement; override;
+
+    function SquareMinusProduct(const x, y: IECFieldElement)
+      : IECFieldElement; override;
+
+    function SquarePlusProduct(const x, y: IECFieldElement)
+      : IECFieldElement; override;
+
+    function SquarePow(pow: Int32): IECFieldElement; override;
+
+    function Equals(const other: IF2mFieldElement): Boolean; reintroduce; overload;
+
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    // /**
+    // * Checks, if the ECFieldElements <code>a</code> and <code>b</code>
+    // * are elements of the same field <code>F<sub>2<sup>m</sup></sub></code>
+    // * (having the same representation).
+    // * @param a field element.
+    // * @param b field element to be compared.
+    // * @throws ArgumentException if <code>a</code> and <code>b</code>
+    // * are not elements of the same field
+    // * <code>F<sub>2<sup>m</sup></sub></code> (having the same
+    // * representation).
+    // */
+    class procedure CheckFieldElements(const a, b: IECFieldElement); static;
+
+    // /**
+    // * @return the representation of the field
+    // * <code>F<sub>2<sup>m</sup></sub></code>, either of
+    // * {@link F2mFieldElement.Tpb} (trinomial
+    // * basis representation) or
+    // * {@link F2mFieldElement.Ppb} (pentanomial
+    // * basis representation).
+    // */
+    property Representation: Int32 read GetRepresentation;
+
+    // /**
+    // * @return the degree <code>m</code> of the reduction polynomial
+    // * <code>f(z)</code>.
+    // */
+    property m: Int32 read GetM;
+    // /**
+    // * @return Tpb: The integer <code>k</code> where <code>x<sup>m</sup> +
+    // * x<sup>k</sup> + 1</code> represents the reduction polynomial
+    // * <code>f(z)</code>.<br/>
+    // * Ppb: The integer <code>k1</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.<br/>
+    // */
+    property k1: Int32 read GetK1;
+    // /**
+    // * @return Tpb: Always returns <code>0</code><br/>
+    // * Ppb: The integer <code>k2</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.<br/>
+    // */
+    property k2: Int32 read GetK2;
+    // /**
+    // * @return Tpb: Always set to <code>0</code><br/>
+    // * Ppb: The integer <code>k3</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.<br/>
+    // */
+    property k3: Int32 read GetK3;
+
+    property ks: TCryptoLibInt32Array read GetKs;
+
+    /// <summary>
+    /// The <c>LongArray</c> holding the bits.
+    /// </summary>
+    property x: TLongArray read GetX;
+
+    property FieldName: string read GetFieldName;
+    property FieldSize: Int32 read GetFieldSize;
+    property BitLength: Int32 read GetBitLength;
+    property IsOne: Boolean read GetIsOne;
+    property IsZero: Boolean read GetIsZero;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Base class for an elliptic curve.
+  /// </summary>
+  TECCurve = class abstract(TInterfacedObject, IECCurve)
+
+  strict private
+
+    class procedure Boot(); static;
+    class constructor CreateECCurve();
+    class destructor DestroyECCurve();
+
+  strict protected
+
+    class var
+
+      FLock: TCriticalSection;
+
+  var
+    Fm_field: IFiniteField;
+    Fm_order, Fm_cofactor: TBigInteger;
+
+    Fm_coord: Int32;
+
+    Fm_endomorphism: IECEndomorphism;
+    Fm_multiplier: IECMultiplier;
+    Fm_a, Fm_b: IECFieldElement;
+
+    constructor Create(const field: IFiniteField);
+
+    procedure SetCoord(const Value: Int32); inline;
+    procedure SetEndomorphism(const Value: IECEndomorphism); inline;
+    procedure SetMultiplier(const Value: IECMultiplier); inline;
+    function GetField: IFiniteField; virtual;
+    function GetA: IECFieldElement; virtual;
+    function GetB: IECFieldElement; virtual;
+    function GetOrder: TBigInteger; virtual;
+    function GetCofactor: TBigInteger; virtual;
+    function GetCoordinateSystem: Int32; virtual;
+
+    function GetFieldSize: Int32; virtual; abstract;
+    function GetInfinity: IECPoint; virtual; abstract;
+
+    function CloneCurve(): IECCurve; virtual; abstract;
+
+    function CreateRawPoint(const x, y: IECFieldElement;
+      withCompression: Boolean): IECPoint; overload; virtual; abstract;
+
+    function CreateRawPoint(const x, y: IECFieldElement;
+      const zs: TCryptoLibGenericArray<IECFieldElement>;
+      withCompression: Boolean): IECPoint; overload; virtual; abstract;
+
+    function CreateDefaultMultiplier(): IECMultiplier; virtual;
+
+    procedure CheckPoint(const point: IECPoint); virtual;
+
+    procedure CheckPoints(const points: TCryptoLibGenericArray<IECPoint>);
+      overload; virtual;
+
+    procedure CheckPoints(const points: TCryptoLibGenericArray<IECPoint>;
+      off, len: Int32); overload; virtual;
+
+    function DecompressPoint(yTilde: Int32; const x1: TBigInteger): IECPoint;
+      virtual; abstract;
+
+  public
+
+    type
+
+    TConfig = class(TInterfacedObject, IConfig)
+
+    strict protected
+    var
+      Fouter: IECCurve;
+      Fcoord: Int32;
+      Fendomorphism: IECEndomorphism;
+      Fmultiplier: IECMultiplier;
+
+    public
+      constructor Create(const outer: IECCurve; coord: Int32;
+        const endomorphism: IECEndomorphism;
+        const multiplier: IECMultiplier); overload;
+
+      destructor Destroy(); override;
+
+      function SetCoordinateSystem(coord: Int32): IConfig; inline;
+      function SetEndomorphism(const endomorphism: IECEndomorphism)
+        : IConfig; inline;
+      function SetMultiplier(const multiplier: IECMultiplier): IConfig; inline;
+      function CreateCurve(): IECCurve;
+
+    end;
+
+  function FromBigInteger(const x: TBigInteger): IECFieldElement;
+    virtual; abstract;
+  function IsValidFieldElement(const x: TBigInteger): Boolean; virtual;
+    abstract;
+
+  function Configure(): IConfig; virtual;
+  function ValidatePoint(const x, y: TBigInteger): IECPoint; overload; virtual;
+
+  function ValidatePoint(const x, y: TBigInteger; withCompression: Boolean)
+    : IECPoint; overload; virtual;
+    deprecated 'Per-point compression property will be removed';
+
+  /// <summary>
+  /// Create a cache-safe lookup table for the specified sequence of points.
+  /// All the points MUST <br />belong to this <c>ECCurve</c> instance, and
+  /// MUST already be normalized.
+  /// </summary>
+  function CreateCacheSafeLookupTable(const points
+    : TCryptoLibGenericArray<IECPoint>; off, len: Int32)
+    : IECLookupTable; virtual;
+
+  function CreatePoint(const x, y: TBigInteger): IECPoint; overload; virtual;
+
+  function CreatePoint(const x, y: TBigInteger; withCompression: Boolean)
+    : IECPoint; overload; virtual;
+    deprecated 'Per-point compression property will be removed';
+
+  function SupportsCoordinateSystem(coord: Int32): Boolean; virtual;
+
+  function GetPreCompInfo(const point: IECPoint; const name: String)
+    : IPreCompInfo; virtual;
+
+  /// <summary>
+  /// Compute a <c>PreCompInfo</c> for a point on this curve, under a given
+  /// name. Used by <c>ECMultiplier</c> to save the precomputation for this <c>
+  /// ECPoint</c> for use by subsequent multiplication.
+  /// </summary>
+  /// <param name="point">
+  /// The <c>ECPoint</c> to store precomputations for.
+  /// </param>
+  /// <param name="name">
+  /// A <c>String</c> used to index precomputations of different types.
+  /// </param>
+  /// <param name="callback">
+  /// Called to calculate the <c>PreCompInfo</c>
+  /// </param>
+  function Precompute(const point: IECPoint; const name: String;
+    const callback: IPreCompCallback): IPreCompInfo; virtual;
+
+  function ImportPoint(const P: IECPoint): IECPoint; virtual;
+
+  /// <summary>
+  /// Normalization ensures that any projective coordinate is 1, and
+  /// therefore that the x, y coordinates reflect those of the equivalent
+  /// point in an affine coordinate system. Where more than one point is to
+  /// be normalized, this method will generally be more efficient than
+  /// normalizing each point separately.
+  /// </summary>
+  /// <param name="points">
+  /// An array of points that will be updated in place with their normalized
+  /// versions, where necessary
+  /// </param>
+  procedure NormalizeAll(const points: TCryptoLibGenericArray<IECPoint>);
+    overload; virtual;
+
+  /// <summary>
+  /// Normalization ensures that any projective coordinate is 1, and
+  /// therefore that the x, y coordinates reflect those of the equivalent
+  /// point in an affine coordinate system. Where more than one point is to
+  /// be normalized, this method will generally be more efficient than
+  /// normalizing each point separately. An (optional) z-scaling factor can
+  /// be applied; effectively each z coordinate is scaled by this value prior
+  /// to normalization (but only one actual multiplication is needed).
+  /// </summary>
+  /// <param name="points">
+  /// An array of points that will be updated in place with their normalized
+  /// versions, where necessary
+  /// </param>
+  /// <param name="off">
+  /// The start of the range of points to normalize
+  /// </param>
+  /// <param name="len">
+  /// The length of the range of points to normalize
+  /// </param>
+  /// <param name="iso">
+  /// The (optional) z-scaling factor - can be null
+  /// </param>
+  procedure NormalizeAll(const points: TCryptoLibGenericArray<IECPoint>;
+    off, len: Int32; const iso: IECFieldElement); overload; virtual;
+
+  function GetEndomorphism(): IECEndomorphism; virtual;
+
+  /// <summary>
+  /// Sets the default <c>ECMultiplier</c>, unless already set.
+  /// </summary>
+  function GetMultiplier(): IECMultiplier; virtual;
+
+  /// <summary>
+  /// Decode a point on this curve from its ASN.1 encoding. The different
+  /// encodings are taken account of, including point compression for <br /><c>
+  /// F</c><b>p</b> (X9.62 s 4.2.1 pg 17).
+  /// </summary>
+  /// <returns>
+  /// The decoded point.
+  /// </returns>
+  function DecodePoint(const encoded: TCryptoLibByteArray): IECPoint; virtual;
+
+  property coord: Int32 write SetCoord;
+  property endomorphism: IECEndomorphism write SetEndomorphism;
+  property multiplier: IECMultiplier write SetMultiplier;
+
+  property FieldSize: Int32 read GetFieldSize;
+
+  property Infinity: IECPoint read GetInfinity;
+
+  property field: IFiniteField read GetField;
+
+  property a: IECFieldElement read GetA;
+
+  property b: IECFieldElement read GetB;
+
+  property Order: TBigInteger read GetOrder;
+
+  property Cofactor: TBigInteger read GetCofactor;
+
+  property CoordinateSystem: Int32 read GetCoordinateSystem;
+
+  function Equals(const other: IECCurve): Boolean; reintroduce;
+  function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+  destructor Destroy; override;
+
+  class function GetAllCoordinateSystems(): TCryptoLibInt32Array;
+    static; inline;
+
+  end;
+
+type
+  TDefaultLookupTable = class(TInterfacedObject, IDefaultLookupTable,
+    IECLookupTable)
+  strict private
+  var
+    Fm_outer: IECCurve;
+    Fm_table: TCryptoLibByteArray;
+    Fm_size: Int32;
+
+  public
+    constructor Create(const outer: IECCurve; const table: TCryptoLibByteArray;
+      size: Int32);
+    function GetSize: Int32; virtual;
+    function Lookup(index: Int32): IECPoint; virtual;
+    property size: Int32 read GetSize;
+
+  end;
+
+type
+  TAbstractFpCurve = class(TECCurve, IAbstractFpCurve)
+
+  strict protected
+
+    constructor Create(const Q: TBigInteger);
+    function DecompressPoint(yTilde: Int32; const x1: TBigInteger)
+      : IECPoint; override;
+
+  public
+    destructor Destroy; override;
+    function IsValidFieldElement(const x: TBigInteger): Boolean; override;
+
+  end;
+
+type
+  TDefaultF2mLookupTable = class(TInterfacedObject, IDefaultF2mLookupTable,
+    IECLookupTable)
+  strict private
+  var
+    Fm_outer: IF2mCurve;
+    Fm_table: TCryptoLibInt64Array;
+    Fm_size: Int32;
+
+  public
+    constructor Create(const outer: IF2mCurve;
+      const table: TCryptoLibInt64Array; size: Int32);
+    function GetSize: Int32; virtual;
+    function Lookup(index: Int32): IECPoint; virtual;
+    property size: Int32 read GetSize;
+
+  end;
+
+type
+  TFpCurve = class(TAbstractFpCurve, IFpCurve)
+
+  strict private
+  const
+    FP_DEFAULT_COORDS = Int32(TECCurveConstants.COORD_JACOBIAN_MODIFIED);
+
+  strict protected
+  var
+    Fm_q, Fm_r: TBigInteger;
+
+    Fm_infinity: IFpPoint;
+
+    constructor Create(const Q, r: TBigInteger; const a, b: IECFieldElement);
+      overload; deprecated 'Use constructor taking order/cofactor';
+    constructor Create(const Q, r: TBigInteger; const a, b: IECFieldElement;
+      const Order, Cofactor: TBigInteger); overload;
+
+    function GetQ: TBigInteger; virtual;
+    function GetInfinity: IECPoint; override;
+    function GetFieldSize: Int32; override;
+
+    function CloneCurve(): IECCurve; override;
+    function CreateRawPoint(const x, y: IECFieldElement;
+      withCompression: Boolean): IECPoint; overload; override;
+
+    function CreateRawPoint(const x, y: IECFieldElement;
+      const zs: TCryptoLibGenericArray<IECFieldElement>;
+      withCompression: Boolean): IECPoint; overload; override;
+
+  public
+    constructor Create(const Q, a, b: TBigInteger); overload;
+      deprecated 'Use constructor taking order/cofactor';
+    constructor Create(const Q, a, b, Order, Cofactor: TBigInteger); overload;
+
+    destructor Destroy; override;
+
+    function FromBigInteger(const x: TBigInteger): IECFieldElement; override;
+    function ImportPoint(const P: IECPoint): IECPoint; override;
+
+    function SupportsCoordinateSystem(coord: Int32): Boolean; override;
+
+    property Q: TBigInteger read GetQ;
+    property Infinity: IECPoint read GetInfinity;
+    property FieldSize: Int32 read GetFieldSize;
+
+  end;
+
+type
+  TAbstractF2mCurve = class abstract(TECCurve, IAbstractF2mCurve)
+
+  strict private
+
+    /// <summary>
+    /// The auxiliary values <c>s</c><b>0</b> and <c>s</c><b>1</b> used for
+    /// partial modular reduction for Koblitz curves.
+    /// </summary>
+    Fsi: TCryptoLibGenericArray<TBigInteger>;
+
+    class function BuildField(m, k1, k2, k3: Int32): IFiniteField; static;
+
+  strict protected
+    constructor Create(m, k1, k2, k3: Int32);
+
+    /// <summary>
+    /// Returns true if this is a Koblitz curve (ABC curve).
+    /// </summary>
+    /// <returns>
+    /// true if this is a Koblitz curve (ABC curve), false otherwise
+    /// </returns>
+    function GetIsKoblitz: Boolean; virtual;
+
+    function DecompressPoint(yTilde: Int32; const x1: TBigInteger)
+      : IECPoint; override;
+
+    // /**
+    // * Solves a quadratic equation <code>z<sup>2</sup> + z = beta</code>(X9.62
+    // * D.1.6) The other solution is <code>z + 1</code>.
+    // *
+    // * @param beta
+    // *            The value to solve the qradratic equation for.
+    // * @return the solution for <code>z<sup>2</sup> + z = beta</code> or
+    // *         <code>null</code> if no solution exists.
+    // */
+    function SolveQuadraticEquation(const beta: IECFieldElement)
+      : IECFieldElement;
+
+  public
+
+    destructor Destroy; override;
+
+    function IsValidFieldElement(const x: TBigInteger): Boolean; override;
+
+    function CreatePoint(const x, y: TBigInteger; withCompression: Boolean)
+      : IECPoint; override;
+      deprecated 'Per-point compression property will be removed';
+
+    // /**
+    // * @return the auxiliary values <code>s<sub>0</sub></code> and
+    // * <code>s<sub>1</sub></code> used for partial modular reduction for
+    // * Koblitz curves.
+    // */
+    function GetSi(): TCryptoLibGenericArray<TBigInteger>; virtual;
+
+    property IsKoblitz: Boolean read GetIsKoblitz;
+
+    class function Inverse(m: Int32; const ks: TCryptoLibInt32Array;
+      const x: TBigInteger): TBigInteger; static; inline;
+
+  end;
+
+type
+  // /**
+  // * Elliptic curves over F2m. The Weierstrass equation is given by
+  // * <code>y<sup>2</sup> + xy = x<sup>3</sup> + ax<sup>2</sup> + b</code>.
+  // */
+  TF2mCurve = class sealed(TAbstractF2mCurve, IF2mCurve)
+
+  strict private
+  const
+    F2M_DEFAULT_COORDS = Int32(TECCurveConstants.COORD_LAMBDA_PROJECTIVE);
+
+  var
+    // /**
+    // * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>.
+    // */
+    Fm: Int32;
+
+    // /**
+    // * TPB: The integer <code>k</code> where <code>x<sup>m</sup> +
+    // * x<sup>k</sup> + 1</code> represents the reduction polynomial
+    // * <code>f(z)</code>.<br/>
+    // * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.<br/>
+    // */
+    Fk1: Int32;
+
+    // /**
+    // * TPB: Always set to <code>0</code><br/>
+    // * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.<br/>
+    // */
+    Fk2: Int32;
+    //
+    // /**
+    // * TPB: Always set to <code>0</code><br/>
+    // * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.<br/>
+    // */
+    Fk3: Int32;
+
+    /// <summary>
+    /// The point at infinity on this curve.
+    /// </summary>
+    Fm_infinity: IF2mPoint;
+
+    constructor Create(m, k1, k2, k3: Int32; const a, b: IECFieldElement;
+      const Order, Cofactor: TBigInteger); overload;
+
+    function GetM: Int32; inline;
+    function GetK1: Int32; inline;
+    function GetK2: Int32; inline;
+    function GetK3: Int32; inline;
+
+  strict protected
+    function GetFieldSize: Int32; override;
+    function GetInfinity: IECPoint; override;
+
+    function CloneCurve(): IECCurve; override;
+    function CreateDefaultMultiplier(): IECMultiplier; override;
+
+    function CreateRawPoint(const x, y: IECFieldElement;
+      withCompression: Boolean): IECPoint; overload; override;
+
+    function CreateRawPoint(const x, y: IECFieldElement;
+      const zs: TCryptoLibGenericArray<IECFieldElement>;
+      withCompression: Boolean): IECPoint; overload; override;
+
+  public
+    // /**
+    // * Constructor for Trinomial Polynomial Basis (TPB).
+    // * @param m  The exponent <code>m</code> of
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // * @param k The integer <code>k</code> where <code>x<sup>m</sup> +
+    // * x<sup>k</sup> + 1</code> represents the reduction
+    // * polynomial <code>f(z)</code>.
+    // * @param a The coefficient <code>a</code> in the Weierstrass equation
+    // * for non-supersingular elliptic curves over
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // * @param b The coefficient <code>b</code> in the Weierstrass equation
+    // * for non-supersingular elliptic curves over
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // */
+    constructor Create(m, K: Int32; const a, b: TBigInteger); overload;
+      deprecated 'Use constructor taking order/cofactor';
+    // /**
+    // * Constructor for Trinomial Polynomial Basis (TPB).
+    // * @param m  The exponent <code>m</code> of
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // * @param k The integer <code>k</code> where <code>x<sup>m</sup> +
+    // * x<sup>k</sup> + 1</code> represents the reduction
+    // * polynomial <code>f(z)</code>.
+    // * @param a The coefficient <code>a</code> in the Weierstrass equation
+    // * for non-supersingular elliptic curves over
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // * @param b The coefficient <code>b</code> in the Weierstrass equation
+    // * for non-supersingular elliptic curves over
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // * @param order The order of the main subgroup of the elliptic curve.
+    // * @param cofactor The cofactor of the elliptic curve, i.e.
+    // * <code>#E<sub>a</sub>(F<sub>2<sup>m</sup></sub>) = h * n</code>.
+    // */
+    constructor Create(m, K: Int32;
+      const a, b, Order, Cofactor: TBigInteger); overload;
+
+    // /**
+    // * Constructor for Pentanomial Polynomial Basis (PPB).
+    // * @param m  The exponent <code>m</code> of
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.
+    // * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.
+    // * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.
+    // * @param a The coefficient <code>a</code> in the Weierstrass equation
+    // * for non-supersingular elliptic curves over
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // * @param b The coefficient <code>b</code> in the Weierstrass equation
+    // * for non-supersingular elliptic curves over
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // */
+
+    constructor Create(m, k1, k2, k3: Int32; const a, b: TBigInteger); overload;
+      deprecated 'Use constructor taking order/cofactor';
+    // /**
+    // * Constructor for Pentanomial Polynomial Basis (PPB).
+    // * @param m  The exponent <code>m</code> of
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.
+    // * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.
+    // * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
+    // * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+    // * represents the reduction polynomial <code>f(z)</code>.
+    // * @param a The coefficient <code>a</code> in the Weierstrass equation
+    // * for non-supersingular elliptic curves over
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // * @param b The coefficient <code>b</code> in the Weierstrass equation
+    // * for non-supersingular elliptic curves over
+    // * <code>F<sub>2<sup>m</sup></sub></code>.
+    // * @param order The order of the main subgroup of the elliptic curve.
+    // * @param cofactor The cofactor of the elliptic curve, i.e.
+    // * <code>#E<sub>a</sub>(F<sub>2<sup>m</sup></sub>) = h * n</code>.
+    // */
+    constructor Create(m, k1, k2, k3: Int32;
+      const a, b, Order, Cofactor: TBigInteger); overload;
+
+    destructor Destroy; override;
+
+    function SupportsCoordinateSystem(coord: Int32): Boolean; override;
+    function FromBigInteger(const x: TBigInteger): IECFieldElement; override;
+
+    /// <summary>
+    /// Return true if curve uses a Trinomial basis.
+    /// </summary>
+    /// <returns>
+    /// return true if curve Trinomial, false otherwise.
+    /// </returns>
+    function IsTrinomial(): Boolean; inline;
+
+    function CreateCacheSafeLookupTable(const points
+      : TCryptoLibGenericArray<IECPoint>; off, len: Int32)
+      : IECLookupTable; override;
+
+    property FieldSize: Int32 read GetFieldSize;
+    property Infinity: IECPoint read GetInfinity;
+    property m: Int32 read GetM;
+    property k1: Int32 read GetK1;
+    property k2: Int32 read GetK2;
+    property k3: Int32 read GetK3;
+
+  end;
+
+type
+
+  /// <summary>
+  /// base class for points on elliptic curves.
+  /// </summary>
+  TECPoint = class abstract(TInterfacedObject, IECPoint)
+
+  strict private
+
+  type
+    IValidityCallback = interface(IPreCompCallback)
+      ['{FD571D52-9852-45A6-BD53-47765EB86F20}']
+
+    end;
+
+  type
+    TValidityCallback = class(TInterfacedObject, IPreCompCallback,
+      IValidityCallback)
+
+    strict private
+    var
+      Fm_outer: IECPoint;
+      Fm_decompressed, Fm_checkOrder: Boolean;
+
+    public
+      constructor Create(const outer: IECPoint;
+        decompressed, checkOrder: Boolean);
+
+      function Precompute(const existing: IPreCompInfo): IPreCompInfo;
+
+    end;
+
+  class constructor ECPoint();
+
+  strict protected
+
+    class var
+
+      FEMPTY_ZS: TCryptoLibGenericArray<IECFieldElement>;
+
+  var
+    Fm_zs: TCryptoLibGenericArray<IECFieldElement>;
+    Fm_withCompression: Boolean;
+    Fm_curve: IECCurve;
+
+    Fm_x, Fm_y: IECFieldElement;
+
+    function GetIsInfinity: Boolean; inline;
+    function GetIsCompressed: Boolean; inline;
+    function GetpreCompTable: TDictionary<String, IPreCompInfo>; inline;
+    procedure SetpreCompTable(const Value: TDictionary<String, IPreCompInfo>); inline;
+    function GetCurve: IECCurve; virtual;
+    function GetCurveCoordinateSystem: Int32; virtual;
+    function GetAffineXCoord: IECFieldElement; virtual;
+    function GetAffineYCoord: IECFieldElement; virtual;
+    function GetXCoord: IECFieldElement; virtual;
+
+    function GetCompressionYTilde: Boolean; virtual; abstract;
+
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      withCompression: Boolean); overload;
+
+    function SatisfiesOrder(): Boolean; virtual;
+    function SatisfiesCurveEquation(): Boolean; virtual; abstract;
+    function Detach(): IECPoint; virtual; abstract;
+
+    function RawXCoord: IECFieldElement; inline;
+
+    function RawYCoord: IECFieldElement; inline;
+
+    function RawZCoords: TCryptoLibGenericArray<IECFieldElement>; inline;
+
+    function CreateScaledPoint(const sx, sy: IECFieldElement)
+      : IECPoint; virtual;
+
+    procedure CheckNormalized(); virtual;
+
+    property CurveCoordinateSystem: Int32 read GetCurveCoordinateSystem;
+
+    property CompressionYTilde: Boolean read GetCompressionYTilde;
+
+    class function GetInitialZCoords(const curve: IECCurve)
+      : TCryptoLibGenericArray<IECFieldElement>; static;
+
+  public
+  var
+    // Dictionary is (string -> PreCompInfo)
+    Fm_preCompTable: TDictionary<String, IPreCompInfo>;
+
+    function GetYCoord: IECFieldElement; virtual;
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      const zs: TCryptoLibGenericArray<IECFieldElement>;
+      withCompression: Boolean); overload;
+    destructor Destroy; override;
+
+    function GetDetachedPoint(): IECPoint; inline;
+    function GetZCoord(index: Int32): IECFieldElement; virtual;
+    function GetZCoords(): TCryptoLibGenericArray<IECFieldElement>; virtual;
+
+    function IsNormalized(): Boolean; virtual;
+
+    /// <summary>
+    /// Normalization ensures that any projective coordinate is 1, and
+    /// therefore that the x, y <br />coordinates reflect those of the
+    /// equivalent point in an affine coordinate system.
+    /// </summary>
+    /// <returns>
+    /// a new ECPoint instance representing the same point, but with
+    /// normalized coordinates
+    /// </returns>
+    function Normalize(): IECPoint; overload; virtual;
+
+    function Normalize(const zInv: IECFieldElement): IECPoint;
+      overload; virtual;
+
+    function ImplIsValid(decompressed, checkOrder: Boolean): Boolean;
+
+    function IsValid(): Boolean; inline;
+    function IsValidPartial(): Boolean; inline;
+
+    function ScaleX(const scale: IECFieldElement): IECPoint; virtual;
+    function ScaleY(const scale: IECFieldElement): IECPoint; virtual;
+
+    function GetEncoded(): TCryptoLibByteArray; overload; virtual;
+    function GetEncoded(compressed: Boolean): TCryptoLibByteArray; overload;
+      virtual; abstract;
+
+    function Add(const b: IECPoint): IECPoint; virtual; abstract;
+    function Subtract(const b: IECPoint): IECPoint; virtual; abstract;
+    function Negate(): IECPoint; virtual; abstract;
+    function TimesPow2(e: Int32): IECPoint; virtual;
+
+    function Twice(): IECPoint; virtual; abstract;
+    function Multiply(b: TBigInteger): IECPoint; virtual; abstract;
+
+    function TwicePlus(const b: IECPoint): IECPoint; virtual;
+
+    function ThreeTimes(): IECPoint; virtual;
+
+    function Equals(const other: IECPoint): Boolean; reintroduce;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    function ToString(): String; override;
+
+    property preCompTable: TDictionary<String, IPreCompInfo>
+      read GetpreCompTable write SetpreCompTable;
+
+    /// <summary>
+    /// Returns the affine x-coordinate after checking that this point is
+    /// normalized.
+    /// </summary>
+    /// <value>
+    /// The affine x-coordinate of this point
+    /// </value>
+    /// <exception cref="ClpCryptoLibTypes|EInvalidOperationCryptoLibException">
+    /// if the point is not normalized
+    /// </exception>
+    property AffineXCoord: IECFieldElement read GetAffineXCoord;
+    /// <summary>
+    /// Returns the affine y-coordinate after checking that this point is
+    /// normalized.
+    /// </summary>
+    /// <value>
+    /// The affine y-coordinate of this point
+    /// </value>
+    /// <exception cref="ClpCryptoLibTypes|EInvalidOperationCryptoLibException">
+    /// if the point is not normalized
+    /// </exception>
+    property AffineYCoord: IECFieldElement read GetAffineYCoord;
+
+    /// <summary>
+    /// Returns the x-coordinate. <br />Caution: depending on the curve's
+    /// coordinate system, this may not be the same value as in an <br />
+    /// affine coordinate system; use Normalize() to get a point where the
+    /// coordinates have their <br />affine values, or use AffineXCoord if
+    /// you expect the point to already have been normalized.
+    /// </summary>
+    /// <value>
+    /// the x-coordinate of this point
+    /// </value>
+    property XCoord: IECFieldElement read GetXCoord;
+    /// <summary>
+    /// Returns the y-coordinate. <br />Caution: depending on the curve's
+    /// coordinate system, this may not be the same value as in an <br />
+    /// affine coordinate system; use Normalize() to get a point where the
+    /// coordinates have their <br />affine values, or use AffineYCoord if
+    /// you expect the point to already have been normalized.
+    /// </summary>
+    /// <value>
+    /// the y-coordinate of this point
+    /// </value>
+    property YCoord: IECFieldElement read GetYCoord;
+
+    property curve: IECCurve read GetCurve;
+
+    property IsInfinity: Boolean read GetIsInfinity;
+
+    property IsCompressed: Boolean read GetIsCompressed;
+
+  end;
+
+type
+  TECPointBase = class abstract(TECPoint, IECPointBase)
+
+  strict protected
+
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      withCompression: Boolean); overload;
+
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      const zs: TCryptoLibGenericArray<IECFieldElement>;
+      withCompression: Boolean); overload;
+
+  public
+
+    destructor Destroy; override;
+
+    /// <summary>
+    /// return the field element encoded with point compression. (S 4.3.6)
+    /// </summary>
+    function GetEncoded(compressed: Boolean): TCryptoLibByteArray; override;
+
+    /// <summary>
+    /// Multiplies this <c>ECPoint</c> by the given number.
+    /// </summary>
+    /// <param name="k">
+    /// The multiplicator.
+    /// </param>
+    /// <returns>
+    /// <c>k * this</c>
+    /// </returns>
+    function Multiply(K: TBigInteger): IECPoint; override;
+
+  end;
+
+type
+  TAbstractFpPoint = class abstract(TECPointBase, IAbstractFpPoint)
+
+  strict protected
+
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      withCompression: Boolean); overload;
+
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      const zs: TCryptoLibGenericArray<IECFieldElement>;
+      withCompression: Boolean); overload;
+
+    function GetCompressionYTilde(): Boolean; override;
+
+    function SatisfiesCurveEquation(): Boolean; override;
+
+    property CompressionYTilde: Boolean read GetCompressionYTilde;
+
+  public
+
+    destructor Destroy; override;
+    function Subtract(const b: IECPoint): IECPoint; override;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Elliptic curve points over Fp
+  /// </summary>
+  TFpPoint = class(TAbstractFpPoint, IFpPoint)
+
+  strict protected
+
+    function Detach(): IECPoint; override;
+
+    function Two(const x: IECFieldElement): IECFieldElement; virtual;
+    function Three(const x: IECFieldElement): IECFieldElement; virtual;
+    function Four(const x: IECFieldElement): IECFieldElement; virtual;
+    function Eight(const x: IECFieldElement): IECFieldElement; virtual;
+    function DoubleProductFromSquares(const a, b, aSquared,
+      bSquared: IECFieldElement): IECFieldElement; virtual;
+
+    function CalculateJacobianModifiedW(const z: IECFieldElement;
+      const ZSquared: IECFieldElement): IECFieldElement; virtual;
+
+    function GetJacobianModifiedW(): IECFieldElement; virtual;
+
+    function TwiceJacobianModified(calculateW: Boolean): IFpPoint; virtual;
+
+  public
+
+    /// <summary>
+    /// Create a point which encodes without point compression.
+    /// </summary>
+    /// <param name="curve">
+    /// curve the curve to use
+    /// </param>
+    /// <param name="x">
+    /// affine x co-ordinate
+    /// </param>
+    /// <param name="y">
+    /// affine y co-ordinate
+    /// </param>
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement);
+      overload; deprecated 'Use ECCurve.CreatePoint to construct points';
+
+    /// <summary>
+    /// Create a point which encodes without point compression.
+    /// </summary>
+    /// <param name="curve">
+    /// curve the curve to use
+    /// </param>
+    /// <param name="x">
+    /// affine x co-ordinate
+    /// </param>
+    /// <param name="y">
+    /// affine y co-ordinate
+    /// </param>
+    /// <param name="withCompression">
+    /// if true encode with point compression
+    /// </param>
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      withCompression: Boolean); overload;
+      deprecated
+      'Per-point compression property will be removed, see GetEncoded(boolean)';
+
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      const zs: TCryptoLibGenericArray<IECFieldElement>;
+      withCompression: Boolean); overload;
+
+    destructor Destroy; override;
+
+    function GetZCoord(index: Int32): IECFieldElement; override;
+    // B.3 pg 62
+    function Add(const b: IECPoint): IECPoint; override;
+
+    // B.3 pg 62
+    function Twice(): IECPoint; override;
+
+    function TwicePlus(const b: IECPoint): IECPoint; override;
+
+    function ThreeTimes(): IECPoint; override;
+
+    function TimesPow2(e: Int32): IECPoint; override;
+
+    function Negate(): IECPoint; override;
+
+  end;
+
+type
+  TAbstractF2mPoint = class abstract(TECPointBase, IAbstractF2mPoint)
+
+  strict protected
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      withCompression: Boolean); overload;
+
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      const zs: TCryptoLibGenericArray<IECFieldElement>;
+      withCompression: Boolean); overload;
+
+    function SatisfiesOrder(): Boolean; override;
+    function SatisfiesCurveEquation(): Boolean; override;
+
+  public
+    destructor Destroy; override;
+    function ScaleX(const scale: IECFieldElement): IECPoint; override;
+    function ScaleY(const scale: IECFieldElement): IECPoint; override;
+
+    function Subtract(const b: IECPoint): IECPoint; override;
+
+    function Tau(): IAbstractF2mPoint; virtual;
+
+    function TauPow(pow: Int32): IAbstractF2mPoint; virtual;
+  end;
+
+type
+
+  /// <summary>
+  /// Elliptic curve points over F2m
+  /// </summary>
+  TF2mPoint = class(TAbstractF2mPoint, IF2mPoint)
+
+  strict protected
+    function GetCompressionYTilde: Boolean; override;
+    function Detach(): IECPoint; override;
+    property CompressionYTilde: Boolean read GetCompressionYTilde;
+  public
+
+    function GetYCoord: IECFieldElement; override;
+
+    /// <param name="curve">
+    /// base curve
+    /// </param>
+    /// <param name="x">
+    /// x point
+    /// </param>
+    /// <param name="y">
+    /// y point
+    /// </param>
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement);
+      overload; deprecated 'Use ECCurve.CreatePoint to construct points';
+
+    /// <param name="curve">
+    /// base curve
+    /// </param>
+    /// <param name="x">
+    /// x point
+    /// </param>
+    /// <param name="y">
+    /// y point
+    /// </param>
+    /// <param name="withCompression">
+    /// true if encode with point compression.
+    /// </param>
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      withCompression: Boolean); overload;
+      deprecated
+      'Per-point compression property will be removed, see GetEncoded(boolean)';
+
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      const zs: TCryptoLibGenericArray<IECFieldElement>;
+      withCompression: Boolean); overload;
+
+    destructor Destroy; override;
+
+    function Add(const b: IECPoint): IECPoint; override;
+
+    function Twice(): IECPoint; override;
+
+    function TwicePlus(const b: IECPoint): IECPoint; override;
+
+    function Negate(): IECPoint; override;
+
+    property YCoord: IECFieldElement read GetYCoord;
+  end;
+
+implementation
+
+{ TF2mFieldElement }
+
+function TF2mFieldElement.GetKs: TCryptoLibInt32Array;
+begin
+  result := FKs;
+end;
+
+function TF2mFieldElement.GetM: Int32;
+begin
+  result := Fm;
+end;
+
+function TF2mFieldElement.GetRepresentation: Int32;
+begin
+  result := Frepresentation;
+end;
+
+function TF2mFieldElement.GetX: TLongArray;
+begin
+  result := Fx;
+end;
+
+function TF2mFieldElement.Add(const b: IECFieldElement): IECFieldElement;
+var
+  iarrClone: TLongArray;
+  bF2m: IF2mFieldElement;
+begin
+  // No check performed here for performance reasons. Instead the
+  // elements involved are checked in ECPoint.F2m
+  // checkFieldElements(this, b);
+  iarrClone := Fx.Copy();
+  bF2m := b as IF2mFieldElement;
+  iarrClone.AddShiftedByWords(bF2m.x, 0);
+  result := TF2mFieldElement.Create(Fm, FKs, iarrClone);
+end;
+
+function TF2mFieldElement.AddOne: IECFieldElement;
+begin
+  result := TF2mFieldElement.Create(Fm, FKs, Fx.AddOne());
+end;
+
+class procedure TF2mFieldElement.CheckFieldElements(const a,
+  b: IECFieldElement);
+var
+  aF2m, bF2m: IF2mFieldElement;
+begin
+  if (not(Supports(a, IF2mFieldElement, aF2m)) or
+    (not(Supports(b, IF2mFieldElement, bF2m)))) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidFieldElement);
+  end;
+
+  if (aF2m.Representation <> bF2m.Representation) then
+  begin
+    // Should never occur
+    raise EArgumentCryptoLibException.CreateRes(@SIncorrectRepresentation);
+  end;
+
+  if ((aF2m.m <> bF2m.m) or (not TArrayUtils.AreEqual(aF2m.ks, bF2m.ks))) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidFieldElements);
+  end;
+end;
+
+constructor TF2mFieldElement.Create(m, K: Int32; const x: TBigInteger);
+begin
+  Create(m, K, 0, 0, x);
+end;
+
+constructor TF2mFieldElement.Create(m, k1, k2, k3: Int32; const x: TBigInteger);
+begin
+  Inherited Create();
+  if (not(x.IsInitialized) or (x.SignValue < 0) or (x.BitLength > m)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidValue2);
+  end;
+
+  if ((k2 = 0) and (k3 = 0)) then
+  begin
+    Frepresentation := Tpb;
+    FKs := TCryptoLibInt32Array.Create(k1);
+  end
+  else
+  begin
+    if (k2 >= k3) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SInvalidK2Value);
+    end;
+    if (k2 <= 0) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SInvalidK2Value2);
+    end;
+
+    Frepresentation := Ppb;
+    FKs := TCryptoLibInt32Array.Create(k1, k2, k3);
+  end;
+
+  Fm := m;
+  Fx := TLongArray.Create(x);
+end;
+
+constructor TF2mFieldElement.Create(m: Int32; const ks: TCryptoLibInt32Array;
+  const x: TLongArray);
+begin
+  Inherited Create();
+  Fm := m;
+  if (System.Length(ks) = 1) then
+  begin
+    Frepresentation := Tpb
+  end
+  else
+  begin
+    Frepresentation := Ppb;
+  end;
+  FKs := ks;
+  Fx := x;
+end;
+
+destructor TF2mFieldElement.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TF2mFieldElement.Divide(const b: IECFieldElement): IECFieldElement;
+var
+  bInv: IECFieldElement;
+begin
+  // There may be more efficient implementations
+  bInv := b.Invert();
+  result := Multiply(bInv);
+end;
+
+function TF2mFieldElement.Equals(const other: IF2mFieldElement): Boolean;
+begin
+  if (other = Self as IF2mFieldElement) then
+  begin
+    result := true;
+    Exit;
+  end;
+  if (Nil = other) then
+  begin
+    result := false;
+    Exit;
+  end;
+  result := ((m = other.m) and (Representation = other.Representation) and
+    TArrayUtils.AreEqual(ks, other.ks) and (x.Equals(other.x)));
+end;
+
+function TF2mFieldElement.GetBitLength: Int32;
+begin
+  result := Fx.Degree();
+end;
+
+function TF2mFieldElement.GetFieldName: String;
+begin
+  result := 'F2m';
+end;
+
+function TF2mFieldElement.GetFieldSize: Int32;
+begin
+  result := Fm;
+end;
+
+function TF2mFieldElement.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := Fx.GetHashCode() xor Fm xor TArrayUtils.GetArrayHashCode(FKs);
+end;
+
+function TF2mFieldElement.GetIsOne: Boolean;
+begin
+  result := Fx.IsOne();
+end;
+
+function TF2mFieldElement.GetIsZero: Boolean;
+begin
+  result := Fx.IsZero();
+end;
+
+function TF2mFieldElement.GetK1: Int32;
+begin
+  result := FKs[0];
+end;
+
+function TF2mFieldElement.GetK2: Int32;
+begin
+  if (System.Length(FKs) >= 2) then
+  begin
+    result := FKs[1];
+  end
+  else
+  begin
+    result := 0;
+  end;
+end;
+
+function TF2mFieldElement.GetK3: Int32;
+begin
+  if (System.Length(FKs) >= 3) then
+  begin
+    result := FKs[2];
+  end
+  else
+  begin
+    result := 0;
+  end;
+end;
+
+function TF2mFieldElement.Invert: IECFieldElement;
+begin
+  result := TF2mFieldElement.Create(Fm, FKs, Fx.ModInverse(Fm, FKs));
+end;
+
+function TF2mFieldElement.Multiply(const b: IECFieldElement): IECFieldElement;
+begin
+  // Right-to-left comb multiplication in the LongArray
+  // Input: Binary polynomials a(z) and b(z) of degree at most m-1
+  // Output: c(z) = a(z) * b(z) mod f(z)
+
+  // No check performed here for performance reasons. Instead the
+  // elements involved are checked in ECPoint.F2m
+  // checkFieldElements(this, b);
+  result := TF2mFieldElement.Create(Fm, FKs,
+    Fx.ModMultiply((b as IF2mFieldElement).x, Fm, FKs));
+end;
+
+function TF2mFieldElement.MultiplyMinusProduct(const b, x, y: IECFieldElement)
+  : IECFieldElement;
+begin
+  result := MultiplyPlusProduct(b, x, y);
+end;
+
+function TF2mFieldElement.MultiplyPlusProduct(const b, x, y: IECFieldElement)
+  : IECFieldElement;
+var
+  ax, bx, xx, yx, ab, xy: TLongArray;
+begin
+  ax := Fx;
+  bx := (b as IF2mFieldElement).x;
+  xx := (x as IF2mFieldElement).x;
+  yx := (y as IF2mFieldElement).x;
+
+  ab := ax.Multiply(bx, Fm, FKs);
+  xy := xx.Multiply(yx, Fm, FKs);
+
+  if ((ab.Equals(ax)) or (ab.Equals(bx))) then
+  begin
+    ab := ab.Copy();
+  end;
+
+  ab.AddShiftedByWords(xy, 0);
+  ab.Reduce(Fm, FKs);
+
+  result := TF2mFieldElement.Create(Fm, FKs, ab);
+end;
+
+function TF2mFieldElement.Negate: IECFieldElement;
+begin
+  // -x == x holds for all x in F2m
+  result := Self as IECFieldElement;
+end;
+
+function TF2mFieldElement.Sqrt: IECFieldElement;
+begin
+  if ((Fx.IsZero()) or (Fx.IsOne())) then
+  begin
+    result := Self as IECFieldElement;
+  end
+  else
+  begin
+    result := SquarePow(Fm - 1);
+  end;
+end;
+
+function TF2mFieldElement.Square: IECFieldElement;
+begin
+  result := TF2mFieldElement.Create(Fm, FKs, Fx.ModSquare(Fm, FKs));
+end;
+
+function TF2mFieldElement.SquareMinusProduct(const x, y: IECFieldElement)
+  : IECFieldElement;
+begin
+  result := SquarePlusProduct(x, y);
+end;
+
+function TF2mFieldElement.SquarePlusProduct(const x, y: IECFieldElement)
+  : IECFieldElement;
+var
+  ax, xx, yx, aa, xy: TLongArray;
+begin
+  ax := Fx;
+  xx := (x as IF2mFieldElement).x;
+  yx := (y as IF2mFieldElement).x;
+
+  aa := ax.Square(Fm, FKs);
+  xy := xx.Multiply(yx, Fm, FKs);
+
+  if (aa.Equals(ax)) then
+  begin
+    aa := aa.Copy();
+  end;
+
+  aa.AddShiftedByWords(xy, 0);
+  aa.Reduce(Fm, FKs);
+
+  result := TF2mFieldElement.Create(Fm, FKs, aa);
+end;
+
+function TF2mFieldElement.SquarePow(pow: Int32): IECFieldElement;
+begin
+  if pow < 1 then
+  begin
+    result := Self as IECFieldElement
+  end
+  else
+  begin
+    result := TF2mFieldElement.Create(Fm, FKs, Fx.ModSquareN(pow, Fm, FKs));
+  end;
+end;
+
+function TF2mFieldElement.Subtract(const b: IECFieldElement): IECFieldElement;
+begin
+  // Addition and subtraction are the same in F2m
+  result := Add(b);
+end;
+
+function TF2mFieldElement.TestBitZero: Boolean;
+begin
+  result := Fx.TestBitZero();
+end;
+
+function TF2mFieldElement.ToBigInteger: TBigInteger;
+begin
+  result := Fx.ToBigInteger();
+end;
+
+{ TECFieldElement }
+
+constructor TECFieldElement.Create;
+begin
+  Inherited Create();
+end;
+
+destructor TECFieldElement.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TECFieldElement.Equals(const other: IECFieldElement): Boolean;
+begin
+  if (other = Self as IECFieldElement) then
+  begin
+    result := true;
+    Exit;
+  end;
+  if (Nil = other) then
+  begin
+    result := false;
+    Exit;
+  end;
+  result := ToBigInteger().Equals(other.ToBigInteger());
+end;
+
+function TECFieldElement.GetBitLength: Int32;
+begin
+  result := ToBigInteger().BitLength;
+end;
+
+function TECFieldElement.GetEncoded: TCryptoLibByteArray;
+begin
+  result := TBigIntegers.AsUnsignedByteArray((FieldSize + 7) div 8,
+    ToBigInteger());
+end;
+
+function TECFieldElement.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := ToBigInteger().GetHashCode();
+end;
+
+function TECFieldElement.GetIsOne: Boolean;
+begin
+  result := BitLength = 1;
+end;
+
+function TECFieldElement.GetIsZero: Boolean;
+begin
+  result := 0 = ToBigInteger().SignValue;
+end;
+
+function TECFieldElement.MultiplyMinusProduct(const b, x, y: IECFieldElement)
+  : IECFieldElement;
+begin
+  result := Multiply(b).Subtract(x.Multiply(y));
+end;
+
+function TECFieldElement.MultiplyPlusProduct(const b, x, y: IECFieldElement)
+  : IECFieldElement;
+begin
+  result := Multiply(b).Add(x.Multiply(y));
+end;
+
+function TECFieldElement.SquareMinusProduct(const x, y: IECFieldElement)
+  : IECFieldElement;
+begin
+  result := Square().Subtract(x.Multiply(y));
+end;
+
+function TECFieldElement.SquarePlusProduct(const x, y: IECFieldElement)
+  : IECFieldElement;
+begin
+  result := Square().Add(x.Multiply(y));
+end;
+
+function TECFieldElement.SquarePow(pow: Int32): IECFieldElement;
+var
+  r: IECFieldElement;
+  i: Int32;
+begin
+  r := Self as IECFieldElement;
+  i := 0;
+  while i < pow do
+  begin
+    r := r.Square();
+    System.Inc(i);
+  end;
+
+  result := r;
+end;
+
+function TECFieldElement.TestBitZero: Boolean;
+begin
+  result := ToBigInteger().TestBit(0);
+end;
+
+function TECFieldElement.ToString: String;
+begin
+  result := ToBigInteger().ToString(16);
+end;
+
+{ TFpFieldElement }
+
+function TFpFieldElement.GetQ: TBigInteger;
+begin
+  result := Fq;
+end;
+
+function TFpFieldElement.GetFieldSize: Int32;
+begin
+  result := Q.BitLength;
+end;
+
+function TFpFieldElement.Add(const b: IECFieldElement): IECFieldElement;
+begin
+  result := TFpFieldElement.Create(Fq, Fr, ModAdd(Fx, b.ToBigInteger()));
+end;
+
+function TFpFieldElement.AddOne: IECFieldElement;
+var
+  x2: TBigInteger;
+begin
+  x2 := Fx.Add(TBigInteger.One);
+  if (x2.CompareTo(Q) = 0) then
+  begin
+    x2 := TBigInteger.Zero;
+  end;
+  result := TFpFieldElement.Create(Fq, Fr, x2);
+end;
+
+class function TFpFieldElement.CalculateResidue(const P: TBigInteger)
+  : TBigInteger;
+var
+  BitLength: Int32;
+  firstWord: TBigInteger;
+begin
+  BitLength := P.BitLength;
+  if (BitLength >= 96) then
+  begin
+    firstWord := P.ShiftRight(BitLength - 64);
+    if (firstWord.Int64Value = Int64(-1)) then
+    begin
+      result := TBigInteger.One.ShiftLeft(BitLength).Subtract(P);
+      Exit;
+    end;
+    if ((BitLength and 7) = 0) then
+    begin
+      result := TBigInteger.One.ShiftLeft(BitLength shl 1).Divide(P).Negate();
+      Exit;
+    end;
+  end;
+  result := Default (TBigInteger);
+end;
+
+function TFpFieldElement.CheckSqrt(const z: IECFieldElement): IECFieldElement;
+begin
+  if (z.Square().Equals(Self as IECFieldElement)) then
+  begin
+    result := z;
+  end
+  else
+  begin
+    result := Nil;
+  end;
+end;
+
+constructor TFpFieldElement.Create(const Q, x: TBigInteger);
+begin
+  Create(Q, CalculateResidue(Q), x);
+end;
+
+constructor TFpFieldElement.Create(const Q, r, x: TBigInteger);
+begin
+  Inherited Create();
+  if (not(x.IsInitialized) or (x.SignValue < 0) or (x.CompareTo(Q) >= 0)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidValue);
+  end;
+
+  Fq := Q;
+  Fr := r;
+  Fx := x;
+end;
+
+destructor TFpFieldElement.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TFpFieldElement.Divide(const b: IECFieldElement): IECFieldElement;
+begin
+  result := TFpFieldElement.Create(Fq, Fr,
+    ModMult(Fx, ModInverse(b.ToBigInteger())));
+end;
+
+function TFpFieldElement.Equals(const other: IFpFieldElement): Boolean;
+begin
+  if (other = Self as IFpFieldElement) then
+  begin
+    result := true;
+    Exit;
+  end;
+
+  if (other = Nil) then
+  begin
+    result := false;
+    Exit;
+  end;
+
+  result := (Q.Equals(other.Q) and (Inherited Equals(other)));
+end;
+
+function TFpFieldElement.GetFieldName: String;
+begin
+  result := 'Fp';
+end;
+
+function TFpFieldElement.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := Q.GetHashCode() xor (Inherited GetHashCode());
+end;
+
+function TFpFieldElement.Invert: IECFieldElement;
+begin
+  // TODO Modular inversion can be faster for a (Generalized) Mersenne Prime.
+  result := TFpFieldElement.Create(Fq, Fr, ModInverse(Fx));
+end;
+
+function TFpFieldElement.LucasSequence(const P, Q, K: TBigInteger)
+  : TCryptoLibGenericArray<TBigInteger>;
+var
+  n, s, j: Int32;
+  Uh, Vl, Vh, Ql, Qh: TBigInteger;
+begin
+  // TODO Research and apply "common-multiplicand multiplication here"
+
+  n := K.BitLength;
+  s := K.GetLowestSetBit();
+
+{$IFDEF DEBUG}
+  System.Assert(K.TestBit(s));
+{$ENDIF DEBUG}
+  Uh := TBigInteger.One;
+  Vl := TBigInteger.Two;
+  Vh := P;
+  Ql := TBigInteger.One;
+  Qh := TBigInteger.One;
+
+  j := n - 1;
+
+  while j >= s + 1 do
+  begin
+    Ql := ModMult(Ql, Qh);
+
+    if (K.TestBit(j)) then
+    begin
+      Qh := ModMult(Ql, Q);
+      Uh := ModMult(Uh, Vh);
+      Vl := ModReduce(Vh.Multiply(Vl).Subtract(P.Multiply(Ql)));
+      Vh := ModReduce(Vh.Multiply(Vh).Subtract(Qh.ShiftLeft(1)));
+    end
+    else
+    begin
+      Qh := Ql;
+      Uh := ModReduce(Uh.Multiply(Vl).Subtract(Ql));
+      Vh := ModReduce(Vh.Multiply(Vl).Subtract(P.Multiply(Ql)));
+      Vl := ModReduce(Vl.Multiply(Vl).Subtract(Ql.ShiftLeft(1)));
+    end;
+    System.Dec(j);
+  end;
+
+  Ql := ModMult(Ql, Qh);
+  Qh := ModMult(Ql, Q);
+  Uh := ModReduce(Uh.Multiply(Vl).Subtract(Ql));
+  Vl := ModReduce(Vh.Multiply(Vl).Subtract(P.Multiply(Ql)));
+  Ql := ModMult(Ql, Qh);
+
+  j := 1;
+
+  while j <= s do
+  begin
+    Uh := ModMult(Uh, Vl);
+    Vl := ModReduce(Vl.Multiply(Vl).Subtract(Ql.ShiftLeft(1)));
+    Ql := ModMult(Ql, Ql);
+    System.Inc(j);
+  end;
+
+  result := TCryptoLibGenericArray<TBigInteger>.Create(Uh, Vl);
+end;
+
+function TFpFieldElement.ModAdd(const x1, x2: TBigInteger): TBigInteger;
+var
+  x3: TBigInteger;
+begin
+  x3 := x1.Add(x2);
+  if (x3.CompareTo(Q) >= 0) then
+  begin
+    x3 := x3.Subtract(Q);
+  end;
+  result := x3;
+end;
+
+function TFpFieldElement.ModDouble(const x: TBigInteger): TBigInteger;
+var
+  _2x: TBigInteger;
+begin
+  _2x := x.ShiftLeft(1);
+  if (_2x.CompareTo(Q) >= 0) then
+  begin
+    _2x := _2x.Subtract(Q);
+  end;
+  result := _2x;
+end;
+
+function TFpFieldElement.ModHalf(const x: TBigInteger): TBigInteger;
+var
+  Lx: TBigInteger;
+begin
+  Lx := x;
+  if (Lx.TestBit(0)) then
+  begin
+    Lx := Q.Add(Lx);
+  end;
+  result := Lx.ShiftRight(1);
+end;
+
+function TFpFieldElement.ModHalfAbs(const x: TBigInteger): TBigInteger;
+var
+  Lx: TBigInteger;
+begin
+  Lx := x;
+  if (Lx.TestBit(0)) then
+  begin
+    Lx := Q.Subtract(Lx);
+  end;
+  result := Lx.ShiftRight(1);
+end;
+
+function TFpFieldElement.ModInverse(const x: TBigInteger): TBigInteger;
+var
+  bits, len: Int32;
+  P, n, z: TCryptoLibUInt32Array;
+begin
+  bits := FieldSize;
+  len := TBits.Asr32((bits + 31), 5);
+  P := TNat.FromBigInteger(bits, Q);
+  n := TNat.FromBigInteger(bits, x);
+  z := TNat.Create(len);
+
+  TMod.Invert(P, n, z);
+
+  result := TNat.ToBigInteger(len, z);
+end;
+
+function TFpFieldElement.ModMult(const x1, x2: TBigInteger): TBigInteger;
+begin
+  result := ModReduce(x1.Multiply(x2));
+end;
+
+function TFpFieldElement.ModReduce(const x: TBigInteger): TBigInteger;
+var
+  negative, rIsOne: Boolean;
+  qLen, d: Int32;
+  qMod, u, v, mu, quot, bk1, Lx: TBigInteger;
+begin
+  Lx := x;
+  if (not(Fr.IsInitialized)) then
+  begin
+    Lx := Lx.&Mod(Q);
+  end
+  else
+  begin
+    negative := Lx.SignValue < 0;
+    if (negative) then
+    begin
+      Lx := Lx.Abs();
+    end;
+    qLen := Q.BitLength;
+    if (Fr.SignValue > 0) then
+    begin
+      qMod := TBigInteger.One.ShiftLeft(qLen);
+      rIsOne := Fr.Equals(TBigInteger.One);
+      while (Lx.BitLength > (qLen + 1)) do
+      begin
+        u := Lx.ShiftRight(qLen);
+        v := Lx.Remainder(qMod);
+        if (not rIsOne) then
+        begin
+          u := u.Multiply(Fr);
+        end;
+        Lx := u.Add(v);
+      end
+    end
+    else
+    begin
+      d := ((qLen - 1) and 31) + 1;
+      mu := Fr.Negate();
+      u := mu.Multiply(Lx.ShiftRight(qLen - d));
+      quot := u.ShiftRight(qLen + d);
+      v := quot.Multiply(Q);
+      bk1 := TBigInteger.One.ShiftLeft(qLen + d);
+      v := v.Remainder(bk1);
+      Lx := Lx.Remainder(bk1);
+      Lx := Lx.Subtract(v);
+      if (Lx.SignValue < 0) then
+      begin
+        Lx := Lx.Add(bk1);
+      end
+    end;
+    while (Lx.CompareTo(Q) >= 0) do
+    begin
+      Lx := Lx.Subtract(Q);
+    end;
+    if ((negative) and (Lx.SignValue <> 0)) then
+    begin
+      Lx := Q.Subtract(Lx);
+    end;
+  end;
+  result := Lx;
+end;
+
+function TFpFieldElement.ModSubtract(const x1, x2: TBigInteger): TBigInteger;
+var
+  x3: TBigInteger;
+begin
+  x3 := x1.Subtract(x2);
+  if (x3.SignValue < 0) then
+  begin
+    x3 := x3.Add(Q);
+  end;
+  result := x3;
+end;
+
+function TFpFieldElement.Multiply(const b: IECFieldElement): IECFieldElement;
+begin
+  result := TFpFieldElement.Create(Fq, Fr, ModMult(Fx, b.ToBigInteger()));
+end;
+
+function TFpFieldElement.MultiplyMinusProduct(const b, x, y: IECFieldElement)
+  : IECFieldElement;
+var
+  ax, bx, xx, yx, ab, xy: TBigInteger;
+begin
+  ax := Fx;
+  bx := b.ToBigInteger();
+  xx := x.ToBigInteger();
+  yx := y.ToBigInteger();
+  ab := ax.Multiply(bx);
+  xy := xx.Multiply(yx);
+  result := TFpFieldElement.Create(Fq, Fr, ModReduce(ab.Subtract(xy)));
+end;
+
+function TFpFieldElement.MultiplyPlusProduct(const b, x, y: IECFieldElement)
+  : IECFieldElement;
+var
+  ax, bx, xx, yx, ab, xy, sum: TBigInteger;
+begin
+  ax := Fx;
+  bx := b.ToBigInteger();
+  xx := x.ToBigInteger();
+  yx := y.ToBigInteger();
+  ab := ax.Multiply(bx);
+  xy := xx.Multiply(yx);
+  sum := ab.Add(xy);
+  if ((Fr.IsInitialized) and (Fr.SignValue < 0) and
+    (sum.BitLength > (Fq.BitLength shl 1))) then
+  begin
+    sum := sum.Subtract(Fq.ShiftLeft(Q.BitLength));
+  end;
+  result := TFpFieldElement.Create(Fq, Fr, ModReduce(sum));
+end;
+
+function TFpFieldElement.Negate: IECFieldElement;
+begin
+  if Fx.SignValue = 0 then
+  begin
+    result := Self as IECFieldElement
+  end
+  else
+  begin
+    result := TFpFieldElement.Create(Fq, Fr, Fq.Subtract(Fx));
+  end;
+end;
+
+function TFpFieldElement.Sqrt: IECFieldElement;
+var
+  u, v, K, e, t1, t2, t3, t4, y, legendreExponent, x, fourX, qMinusOne,
+    P: TBigInteger;
+  tempRes: TCryptoLibGenericArray<TBigInteger>;
+  CompareRes, ModReduceRes: Boolean;
+begin
+  if (IsZero or IsOne) then
+  begin
+    result := Self as IECFieldElement;
+    Exit;
+  end;
+
+  if (not Fq.TestBit(0)) then
+  begin
+    raise ENotImplementedCryptoLibException.CreateRes(@SEvenValue);
+  end;
+
+  if (Fq.TestBit(1)) then // q == 4m + 3
+  begin
+    e := Fq.ShiftRight(2).Add(TBigInteger.One);
+    result := CheckSqrt(TFpFieldElement.Create(Fq, Fr, Fx.ModPow(e, Fq))
+      as IFpFieldElement);
+    Exit;
+  end;
+
+  if (Fq.TestBit(2)) then // q == 8m + 5
+  begin
+    t1 := Fx.ModPow(Fq.ShiftRight(3), Fq);
+    t2 := ModMult(t1, Fx);
+    t3 := ModMult(t2, t1);
+
+    if (t3.Equals(TBigInteger.One)) then
+    begin
+      result := CheckSqrt(TFpFieldElement.Create(Fq, Fr, t2)
+        as IFpFieldElement);
+      Exit;
+    end;
+
+    // TODO This is constant and could be precomputed
+    t4 := TBigInteger.Two.ModPow(Fq.ShiftRight(2), Fq);
+
+    y := ModMult(t2, t4);
+
+    result := CheckSqrt(TFpFieldElement.Create(Fq, Fr, y) as IFpFieldElement);
+    Exit;
+  end;
+
+  // q == 8m + 1
+
+  legendreExponent := Fq.ShiftRight(1);
+  if (not(Fx.ModPow(legendreExponent, Fq).Equals(TBigInteger.One))) then
+  begin
+    result := Nil;
+    Exit;
+  end;
+
+  x := Fx;
+  fourX := ModDouble(ModDouble(x));
+
+  K := legendreExponent.Add(TBigInteger.One);
+  qMinusOne := Fq.Subtract(TBigInteger.One);
+
+  repeat
+
+    repeat
+      P := TBigInteger.Arbitrary(Fq.BitLength);
+
+      CompareRes := P.CompareTo(Q) >= 0;
+      ModReduceRes := (not ModReduce(P.Multiply(P).Subtract(fourX))
+        .ModPow(legendreExponent, Q).Equals(qMinusOne));
+
+    until ((not CompareRes) and (not ModReduceRes));
+
+    tempRes := LucasSequence(P, x, K);
+    u := tempRes[0];
+    v := tempRes[1];
+
+    if (ModMult(v, v).Equals(fourX)) then
+    begin
+      result := TFpFieldElement.Create(Fq, Fr, ModHalfAbs(v));
+      Exit;
+    end;
+
+  until ((not u.Equals(TBigInteger.One)) or (not u.Equals(qMinusOne)));
+  result := Nil;
+end;
+
+function TFpFieldElement.Square: IECFieldElement;
+begin
+  result := TFpFieldElement.Create(Fq, Fr, ModMult(Fx, Fx));
+end;
+
+function TFpFieldElement.SquareMinusProduct(const x, y: IECFieldElement)
+  : IECFieldElement;
+var
+  ax, xx, yx, aa, xy: TBigInteger;
+begin
+  ax := Fx;
+  xx := x.ToBigInteger();
+  yx := y.ToBigInteger();
+  aa := ax.Multiply(ax);
+  xy := xx.Multiply(yx);
+  result := TFpFieldElement.Create(Fq, Fr, ModReduce(aa.Subtract(xy)));
+end;
+
+function TFpFieldElement.SquarePlusProduct(const x, y: IECFieldElement)
+  : IECFieldElement;
+var
+  ax, xx, yx, aa, xy, sum: TBigInteger;
+begin
+  ax := Fx;
+  xx := x.ToBigInteger();
+  yx := y.ToBigInteger();
+  aa := ax.Multiply(ax);
+  xy := xx.Multiply(yx);
+  sum := aa.Add(xy);
+  if ((Fr.IsInitialized) and (Fr.SignValue < 0) and
+    (sum.BitLength > (Fq.BitLength shl 1))) then
+  begin
+    sum := sum.Subtract(Fq.ShiftLeft(Fq.BitLength));
+  end;
+  result := TFpFieldElement.Create(Fq, Fr, ModReduce(sum));
+end;
+
+function TFpFieldElement.Subtract(const b: IECFieldElement): IECFieldElement;
+begin
+  result := TFpFieldElement.Create(Fq, Fr, ModSubtract(Fx, b.ToBigInteger()));
+end;
+
+function TFpFieldElement.ToBigInteger: TBigInteger;
+begin
+  result := Fx;
+end;
+
+{ TAbstract2mFieldElement }
+
+function TAbstractF2mFieldElement.HalfTrace: IECFieldElement;
+var
+  m, i: Int32;
+  fe, ht: IECFieldElement;
+begin
+  m := FieldSize;
+  if ((m and 1) = 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SHalfTraceUndefinedForM);
+  end;
+
+  fe := Self as IECFieldElement;
+  ht := fe;
+  i := 2;
+  while i < m do
+  begin
+    fe := fe.SquarePow(2);
+    ht := ht.Add(fe);
+    System.Inc(i, 2);
+  end;
+
+  result := ht;
+end;
+
+function TAbstractF2mFieldElement.Trace: Int32;
+var
+  m, i: Int32;
+  fe, tr: IECFieldElement;
+begin
+  m := FieldSize;
+  fe := Self as IECFieldElement;
+  tr := fe;
+
+  i := 1;
+  while i < m do
+  begin
+    fe := fe.Square();
+    tr := tr.Add(fe);
+    System.Inc(i);
+  end;
+
+  if (tr.IsZero) then
+  begin
+    result := 0;
+    Exit;
+  end;
+  if (tr.IsOne) then
+  begin
+    result := 1;
+    Exit;
+  end;
+  raise EArgumentCryptoLibException.CreateRes(@STraceInternalErrorCalculation);
+end;
+
+{ TECCurve }
+
+class procedure TECCurve.Boot;
+begin
+  if FLock = Nil then
+  begin
+    FLock := TCriticalSection.Create;
+  end;
+end;
+
+procedure TECCurve.CheckPoint(const point: IECPoint);
+begin
+  if ((point = Nil) or ((Self as IECCurve) <> point.curve)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidPointOnCurve);
+  end;
+end;
+
+procedure TECCurve.CheckPoints(const points: TCryptoLibGenericArray<IECPoint>);
+begin
+  CheckPoints(points, 0, System.Length(points));
+end;
+
+procedure TECCurve.CheckPoints(const points: TCryptoLibGenericArray<IECPoint>;
+  off, len: Int32);
+var
+  i: Int32;
+  point: IECPoint;
+begin
+  if (points = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SPointsNil);
+  end;
+  if ((off < 0) or (len < 0) or (off > (System.Length(points) - len))) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidRangeSpecified);
+  end;
+
+  for i := 0 to System.Pred(len) do
+
+  begin
+    point := points[off + i];
+    if ((point <> Nil) and ((Self as IECCurve) <> point.curve)) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SInvalidPointOnCurve2);
+    end;
+  end;
+end;
+
+function TECCurve.Configure: IConfig;
+begin
+  result := TConfig.Create(Self as IECCurve, Self.Fm_coord,
+    Self.Fm_endomorphism, Self.Fm_multiplier);
+end;
+
+constructor TECCurve.Create(const field: IFiniteField);
+begin
+  inherited Create();
+  Fm_field := field;
+end;
+
+function TECCurve.CreateCacheSafeLookupTable(const points
+  : TCryptoLibGenericArray<IECPoint>; off, len: Int32): IECLookupTable;
+var
+  FE_BYTES, position, i, pxStart, pyStart, pxLen, pyLen: Int32;
+  table, px, py: TCryptoLibByteArray;
+  P: IECPoint;
+begin
+  FE_BYTES := (FieldSize + 7) div 8;
+  System.SetLength(table, len * FE_BYTES * 2);
+  position := 0;
+
+  for i := 0 to System.Pred(len) do
+  begin
+    P := points[off + i];
+    px := P.RawXCoord.ToBigInteger().ToByteArray();
+    py := P.RawYCoord.ToBigInteger().ToByteArray();
+
+    if System.Length(px) > FE_BYTES then
+    begin
+      pxStart := 1
+    end
+    else
+    begin
+      pxStart := 0
+    end;
+
+    pxLen := System.Length(px) - pxStart;
+
+    if System.Length(py) > FE_BYTES then
+    begin
+      pyStart := 1
+    end
+    else
+    begin
+      pyStart := 0
+    end;
+
+    pyLen := System.Length(py) - pyStart;
+
+    System.Move(px[pxStart], table[position + FE_BYTES - pxLen],
+      pxLen * System.SizeOf(Byte));
+    position := position + FE_BYTES;
+
+    System.Move(py[pyStart], table[position + FE_BYTES - pyLen],
+      pyLen * System.SizeOf(Byte));
+    position := position + FE_BYTES;
+  end;
+  result := TDefaultLookupTable.Create(Self as IECCurve, table, len);
+end;
+
+function TECCurve.CreateDefaultMultiplier: IECMultiplier;
+var
+  glvEndomorphism: IGlvEndomorphism;
+begin
+  if (Supports(Fm_endomorphism, IGlvEndomorphism, glvEndomorphism)) then
+  begin
+    result := TGlvMultiplier.Create(Self as IECCurve, glvEndomorphism);
+    Exit;
+  end;
+
+  result := TWNafL2RMultiplier.Create();
+end;
+
+class constructor TECCurve.CreateECCurve;
+begin
+  TECCurve.Boot;
+end;
+
+function TECCurve.CreatePoint(const x, y: TBigInteger): IECPoint;
+begin
+  result := CreatePoint(x, y, false);
+end;
+
+function TECCurve.CreatePoint(const x, y: TBigInteger; withCompression: Boolean)
+  : IECPoint;
+begin
+  result := CreateRawPoint(FromBigInteger(x), FromBigInteger(y),
+    withCompression);
+end;
+
+function TECCurve.DecodePoint(const encoded: TCryptoLibByteArray): IECPoint;
+var
+  x, y: TBigInteger;
+  P: IECPoint;
+  expectedLength, yTilde: Int32;
+  ltype: Byte;
+begin
+  P := Nil;
+  expectedLength := (FieldSize + 7) div 8;
+
+  ltype := encoded[0];
+  case ltype of
+    $00: // infinity
+      begin
+        if (System.Length(encoded) <> 1) then
+        begin
+          raise EArgumentCryptoLibException.CreateRes
+            (@SIncorrectLengthInfinityEncoding);
+        end;
+
+        P := Infinity;
+      end;
+
+    $02, // compressed
+    $03: // compressed
+      begin
+        if (System.Length(encoded) <> (expectedLength + 1)) then
+        begin
+          raise EArgumentCryptoLibException.CreateRes
+            (@SIncorrectLengthCompressedEncoding);
+        end;
+
+        yTilde := ltype and 1;
+        x := TBigInteger.Create(1, encoded, 1, expectedLength);
+
+        P := DecompressPoint(yTilde, x);
+        // TODO Skip curve equation check?
+        if ((not P.ImplIsValid(true, true))) then
+        begin
+          raise EArgumentCryptoLibException.CreateRes(@SInvalidPoint);
+        end;
+      end;
+
+    $04: // uncompressed
+      begin
+        if (System.Length(encoded) <> ((2 * expectedLength) + 1)) then
+        begin
+          raise EArgumentCryptoLibException.CreateRes
+            (@SIncorrectLengthUnCompressedEncoding);
+        end;
+
+        x := TBigInteger.Create(1, encoded, 1, expectedLength);
+        y := TBigInteger.Create(1, encoded, 1 + expectedLength, expectedLength);
+
+        P := ValidatePoint(x, y);
+      end;
+
+    $06, // hybrid
+    $07: // hybrid
+      begin
+        if (System.Length(encoded) <> ((2 * expectedLength) + 1)) then
+        begin
+          raise EArgumentCryptoLibException.CreateRes
+            (@SIncorrectLengthHybridEncoding);
+        end;
+
+        x := TBigInteger.Create(1, encoded, 1, expectedLength);
+        y := TBigInteger.Create(1, encoded, 1 + expectedLength, expectedLength);
+
+        if ((y.TestBit(0)) <> (ltype = $07)) then
+        begin
+          raise EArgumentCryptoLibException.CreateRes(@SInConsistentYCoord);
+        end;
+
+        P := ValidatePoint(x, y);
+      end
+
+  else
+    begin
+      raise EFormatCryptoLibException.CreateResFmt
+        (@SInvalidPointEncoding, [ltype]);
+    end;
+
+  end;
+
+  if ((ltype <> $00) and (P.IsInfinity)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidInfinityEncoding);
+  end;
+
+  result := P;
+end;
+
+destructor TECCurve.Destroy;
+begin
+  inherited Destroy;
+end;
+
+class destructor TECCurve.DestroyECCurve;
+begin
+  FLock.Free;
+end;
+
+function TECCurve.Equals(const other: IECCurve): Boolean;
+begin
+  if ((Self as IECCurve) = other) then
+  begin
+    result := true;
+    Exit;
+  end;
+  if (other = Nil) then
+  begin
+    result := false;
+    Exit;
+  end;
+  result := (field as TObject).Equals(other.field as TObject) and
+    (a.ToBigInteger().Equals(other.a.ToBigInteger())) and
+    (b.ToBigInteger().Equals(other.b.ToBigInteger()));
+end;
+
+function TECCurve.GetA: IECFieldElement;
+begin
+  result := Fm_a;
+end;
+
+class function TECCurve.GetAllCoordinateSystems: TCryptoLibInt32Array;
+begin
+  result := TCryptoLibInt32Array.Create(TECCurveConstants.COORD_AFFINE,
+    TECCurveConstants.COORD_HOMOGENEOUS, TECCurveConstants.COORD_JACOBIAN,
+    TECCurveConstants.COORD_JACOBIAN_CHUDNOVSKY,
+    TECCurveConstants.COORD_JACOBIAN_MODIFIED,
+    TECCurveConstants.COORD_LAMBDA_AFFINE,
+    TECCurveConstants.COORD_LAMBDA_PROJECTIVE, TECCurveConstants.COORD_SKEWED);
+end;
+
+function TECCurve.GetB: IECFieldElement;
+begin
+  result := Fm_b;
+end;
+
+function TECCurve.GetCofactor: TBigInteger;
+begin
+  result := Fm_cofactor;
+end;
+
+function TECCurve.GetCoordinateSystem: Int32;
+begin
+  result := Fm_coord;
+end;
+
+function TECCurve.GetEndomorphism: IECEndomorphism;
+begin
+  result := Fm_endomorphism;
+end;
+
+function TECCurve.GetField: IFiniteField;
+begin
+  result := Fm_field;
+end;
+
+function TECCurve.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := (field as TObject).GetHashCode()
+    xor Int32(TBits.RotateLeft32(a.ToBigInteger().GetHashCode(), 8))
+    xor Int32(TBits.RotateLeft32(b.ToBigInteger().GetHashCode(), 16));
+end;
+
+function TECCurve.GetMultiplier: IECMultiplier;
+begin
+
+  FLock.Acquire;
+  try
+    if (Fm_multiplier = Nil) then
+    begin
+      Fm_multiplier := CreateDefaultMultiplier();
+    end;
+    result := Fm_multiplier;
+  finally
+    FLock.Release;
+  end;
+
+end;
+
+function TECCurve.GetOrder: TBigInteger;
+begin
+  result := Fm_order;
+end;
+
+function TECCurve.GetPreCompInfo(const point: IECPoint; const name: String)
+  : IPreCompInfo;
+var
+  table: TDictionary<String, IPreCompInfo>;
+begin
+  CheckPoint(point);
+
+  FLock.Acquire;
+  try
+    table := point.preCompTable;
+    if table = Nil then
+    begin
+      result := Nil;
+    end
+    else
+    begin
+      table.TryGetValue(name, result);
+    end;
+  finally
+    FLock.Release;
+  end;
+end;
+
+function TECCurve.ImportPoint(const P: IECPoint): IECPoint;
+var
+  Lp: IECPoint;
+begin
+  if ((Self as IECCurve) = P.curve) then
+  begin
+    result := P;
+    Exit;
+  end;
+  if (P.IsInfinity) then
+  begin
+    result := Infinity;
+    Exit;
+  end;
+
+  // TODO Default behaviour could be improved if the two curves have the same coordinate system by copying any Z coordinates.
+  Lp := P.Normalize();
+
+  result := CreatePoint(Lp.XCoord.ToBigInteger(), Lp.YCoord.ToBigInteger(),
+    Lp.IsCompressed);
+end;
+
+procedure TECCurve.NormalizeAll(const points: TCryptoLibGenericArray<IECPoint>;
+  off, len: Int32; const iso: IECFieldElement);
+var
+  zs: TCryptoLibGenericArray<IECFieldElement>;
+  indices: TCryptoLibInt32Array;
+  count, i, j, index: Int32;
+  P: IECPoint;
+begin
+  CheckPoints(points, off, len);
+  case CoordinateSystem of
+    TECCurveConstants.COORD_AFFINE, TECCurveConstants.COORD_LAMBDA_AFFINE:
+      begin
+        if (iso <> Nil) then
+        begin
+          raise EArgumentCryptoLibException.CreateRes
+            (@SInvalidAffineCoordinates);
+        end;
+
+        Exit;
+      end;
+  end;
+
+  // /*
+  // * Figure out which of the points actually need to be normalized
+  // */
+  System.SetLength(zs, len);
+  System.SetLength(indices, len);
+
+  count := 0;
+  for i := 0 to System.Pred(len) do
+
+  begin
+    P := points[off + i];
+    if ((P <> Nil) and ((iso <> Nil) or (not(P.IsNormalized())))) then
+    begin
+      zs[count] := P.GetZCoord(0);
+      indices[count] := off + i;
+      System.Inc(count);
+    end;
+  end;
+
+  if (count = 0) then
+  begin
+    Exit;
+  end;
+
+  TECAlgorithms.MontgomeryTrick(zs, 0, count, iso);
+
+  for j := 0 to System.Pred(count) do
+
+  begin
+    index := indices[j];
+    points[index] := points[index].Normalize(zs[j]);
+  end;
+
+end;
+
+procedure TECCurve.NormalizeAll(const points: TCryptoLibGenericArray<IECPoint>);
+begin
+  NormalizeAll(points, 0, System.Length(points), Nil);
+end;
+
+procedure TECCurve.SetCoord(const Value: Int32);
+begin
+  Fm_coord := Value;
+end;
+
+procedure TECCurve.SetEndomorphism(const Value: IECEndomorphism);
+begin
+  Fm_endomorphism := Value;
+end;
+
+procedure TECCurve.SetMultiplier(const Value: IECMultiplier);
+begin
+  Fm_multiplier := Value;
+end;
+
+function TECCurve.Precompute(const point: IECPoint; const name: String;
+  const callback: IPreCompCallback): IPreCompInfo;
+var
+  table: TDictionary<String, IPreCompInfo>;
+  existing: IPreCompInfo;
+begin
+  CheckPoint(point);
+
+  FLock.Acquire;
+  try
+    table := point.preCompTable;
+    if table = Nil then
+    begin
+      table := TDictionary<String, IPreCompInfo>.Create(4);
+      point.preCompTable := table;
+    end;
+
+    table.TryGetValue(name, existing);
+
+    result := callback.Precompute(existing);
+
+    if (result <> existing) then
+    begin
+      table.AddOrSetValue(name, result);
+    end;
+
+  finally
+    FLock.Release;
+  end;
+end;
+
+function TECCurve.SupportsCoordinateSystem(coord: Int32): Boolean;
+begin
+  result := coord = TECCurveConstants.COORD_AFFINE;
+end;
+
+function TECCurve.ValidatePoint(const x, y: TBigInteger;
+  withCompression: Boolean): IECPoint;
+var
+  P: IECPoint;
+begin
+  P := CreatePoint(x, y, withCompression);
+  if (not P.IsValid()) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidPointCoordinates);
+  end;
+  result := P;
+end;
+
+function TECCurve.ValidatePoint(const x, y: TBigInteger): IECPoint;
+var
+  P: IECPoint;
+begin
+  P := CreatePoint(x, y);
+  if (not P.IsValid()) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidPointCoordinates);
+  end;
+  result := P;
+end;
+
+{ TECCurve.TConfig }
+
+constructor TECCurve.TConfig.Create(const outer: IECCurve; coord: Int32;
+  const endomorphism: IECEndomorphism; const multiplier: IECMultiplier);
+begin
+  Inherited Create();
+  Fouter := outer;
+  Fcoord := coord;
+  Fendomorphism := endomorphism;
+  Fmultiplier := multiplier;
+end;
+
+function TECCurve.TConfig.CreateCurve: IECCurve;
+var
+  c: IECCurve;
+begin
+  if (not Fouter.SupportsCoordinateSystem(Fcoord)) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SUnSupportedCoordinateSystem);
+  end;
+
+  c := Fouter.CloneCurve();
+  if (c = Fouter) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes(@SCurrentCurve);
+  end;
+
+  c.coord := Fcoord;
+  c.endomorphism := Fendomorphism;
+  c.multiplier := Fmultiplier;
+
+  result := c;
+end;
+
+destructor TECCurve.TConfig.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TECCurve.TConfig.SetCoordinateSystem(coord: Int32): IConfig;
+begin
+  Fcoord := coord;
+  result := Self as IConfig;
+end;
+
+function TECCurve.TConfig.SetEndomorphism(const endomorphism
+  : IECEndomorphism): IConfig;
+begin
+  Fendomorphism := endomorphism;
+  result := Self as IConfig;
+end;
+
+function TECCurve.TConfig.SetMultiplier(const multiplier
+  : IECMultiplier): IConfig;
+begin
+  Fmultiplier := multiplier;
+  result := Self as IConfig;
+end;
+
+{ TAbstractFpCurve }
+
+constructor TAbstractFpCurve.Create(const Q: TBigInteger);
+begin
+  Inherited Create(TFiniteFields.GetPrimeField(Q));
+end;
+
+function TAbstractFpCurve.DecompressPoint(yTilde: Int32; const x1: TBigInteger)
+  : IECPoint;
+var
+  x, rhs, y: IECFieldElement;
+begin
+  x := FromBigInteger(x1);
+  rhs := x.Square().Add(a).Multiply(x).Add(b);
+  y := rhs.Sqrt();
+
+  // /*
+  // * If y is not a square, then we haven't got a point on the curve
+  // */
+  if (y = Nil) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidPointCompression);
+  end;
+
+  if (y.TestBitZero() <> (yTilde = 1)) then
+  begin
+    // Use the other root
+    y := y.Negate();
+  end;
+
+  result := CreateRawPoint(x, y, true);
+end;
+
+destructor TAbstractFpCurve.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TAbstractFpCurve.IsValidFieldElement(const x: TBigInteger): Boolean;
+begin
+  result := (x.IsInitialized) and (x.SignValue >= 0) and
+    (x.CompareTo(field.Characteristic) < 0);
+end;
+
+{ TFpCurve }
+
+function TFpCurve.CloneCurve: IECCurve;
+begin
+  result := TFpCurve.Create(Fm_q, Fm_r, Fm_a, Fm_b, Fm_order, Fm_cofactor);
+end;
+
+constructor TFpCurve.Create(const Q, r: TBigInteger;
+  const a, b: IECFieldElement; const Order, Cofactor: TBigInteger);
+begin
+  Inherited Create(Q);
+  Fm_q := Q;
+  Fm_r := r;
+  Fm_infinity := TFpPoint.Create(Self as IECCurve, Nil, Nil, false);
+
+  Fm_a := a;
+  Fm_b := b;
+  Fm_order := Order;
+  Fm_cofactor := Cofactor;
+  Fm_coord := FP_DEFAULT_COORDS;
+end;
+
+constructor TFpCurve.Create(const Q, r: TBigInteger;
+  const a, b: IECFieldElement);
+begin
+  Create(Q, r, a, b, Default (TBigInteger), Default (TBigInteger));
+end;
+
+constructor TFpCurve.Create(const Q, a, b, Order, Cofactor: TBigInteger);
+begin
+  Inherited Create(Q);
+  Fm_q := Q;
+  Fm_r := TFpFieldElement.CalculateResidue(Q);
+  Fm_infinity := TFpPoint.Create(Self as IECCurve, Nil, Nil, false);
+
+  Fm_a := FromBigInteger(a);
+  Fm_b := FromBigInteger(b);
+  Fm_order := Order;
+  Fm_cofactor := Cofactor;
+  Fm_coord := FP_DEFAULT_COORDS;
+end;
+
+constructor TFpCurve.Create(const Q, a, b: TBigInteger);
+begin
+  Create(Q, a, b, Default (TBigInteger), Default (TBigInteger));
+end;
+
+function TFpCurve.CreateRawPoint(const x, y: IECFieldElement;
+  const zs: TCryptoLibGenericArray<IECFieldElement>; withCompression: Boolean)
+  : IECPoint;
+begin
+  result := TFpPoint.Create(Self as IECCurve, x, y, zs, withCompression);
+end;
+
+destructor TFpCurve.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TFpCurve.CreateRawPoint(const x, y: IECFieldElement;
+  withCompression: Boolean): IECPoint;
+begin
+  result := TFpPoint.Create(Self as IECCurve, x, y, withCompression);
+end;
+
+function TFpCurve.FromBigInteger(const x: TBigInteger): IECFieldElement;
+begin
+  result := TFpFieldElement.Create(Fm_q, Fm_r, x);
+end;
+
+function TFpCurve.GetFieldSize: Int32;
+begin
+  result := Fm_q.BitLength;
+end;
+
+function TFpCurve.GetInfinity: IECPoint;
+begin
+  result := Fm_infinity;
+end;
+
+function TFpCurve.GetQ: TBigInteger;
+begin
+  result := Fm_q;
+end;
+
+function TFpCurve.ImportPoint(const P: IECPoint): IECPoint;
+begin
+  if ((Self as IECCurve <> P.curve) and
+    (CoordinateSystem = TECCurveConstants.COORD_JACOBIAN) and (not P.IsInfinity))
+  then
+  begin
+    case P.curve.CoordinateSystem of
+      TECCurveConstants.COORD_JACOBIAN,
+        TECCurveConstants.COORD_JACOBIAN_CHUDNOVSKY,
+        TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+        begin
+          result := TFpPoint.Create(Self as IECCurve,
+            FromBigInteger(P.RawXCoord.ToBigInteger()),
+            FromBigInteger(P.RawYCoord.ToBigInteger()),
+            TCryptoLibGenericArray<IECFieldElement>.Create
+            (FromBigInteger(P.GetZCoord(0).ToBigInteger())), P.IsCompressed);
+          Exit;
+        end;
+    end;
+  end;
+
+  result := (Inherited ImportPoint(P));
+end;
+
+function TFpCurve.SupportsCoordinateSystem(coord: Int32): Boolean;
+begin
+  case coord of
+    TECCurveConstants.COORD_AFFINE, TECCurveConstants.COORD_HOMOGENEOUS,
+      TECCurveConstants.COORD_JACOBIAN,
+      TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+      begin
+        result := true;
+      end
+  else
+    begin
+      result := false;
+    end;
+  end;
+end;
+
+{ TAbstractF2mCurve }
+
+class function TAbstractF2mCurve.BuildField(m, k1, k2, k3: Int32): IFiniteField;
+begin
+  if (k1 = 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidK1);
+  end;
+
+  if (k2 = 0) then
+  begin
+    if (k3 <> 0) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SInvalidK3);
+    end;
+
+    result := TFiniteFields.GetBinaryExtensionField
+      (TCryptoLibInt32Array.Create(0, k1, m));
+    Exit;
+  end;
+
+  if (k2 <= k1) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SK2K1MisMatch);
+  end;
+
+  if (k3 <= k2) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SK3K2Mismatch);
+  end;
+
+  result := TFiniteFields.GetBinaryExtensionField(TCryptoLibInt32Array.Create(0,
+    k1, k2, k3, m));
+end;
+
+constructor TAbstractF2mCurve.Create(m, k1, k2, k3: Int32);
+begin
+  Inherited Create(BuildField(m, k1, k2, k3));
+end;
+
+function TAbstractF2mCurve.CreatePoint(const x, y: TBigInteger;
+  withCompression: Boolean): IECPoint;
+var
+  Lx, LY: IECFieldElement;
+begin
+  Lx := FromBigInteger(x);
+  LY := FromBigInteger(y);
+
+  case CoordinateSystem of
+    TECCurveConstants.COORD_LAMBDA_AFFINE,
+      TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        if (Lx.IsZero) then
+        begin
+          if (not LY.Square().Equals(b)) then
+          begin
+            raise EArgumentCryptoLibException.Create('');
+          end;
+        end
+        else
+        begin
+          // Y becomes Lambda (X + Y/X) here
+          LY := LY.Divide(Lx).Add(Lx);
+        end;
+      end;
+  end;
+
+  result := CreateRawPoint(Lx, LY, withCompression);
+end;
+
+function TAbstractF2mCurve.DecompressPoint(yTilde: Int32; const x1: TBigInteger)
+  : IECPoint;
+var
+  xp, yp, beta, z: IECFieldElement;
+begin
+  xp := FromBigInteger(x1);
+  yp := Nil;
+  if (xp.IsZero) then
+  begin
+    yp := b.Sqrt();
+  end
+  else
+  begin
+    beta := xp.Square().Invert().Multiply(b).Add(a).Add(xp);
+    z := SolveQuadraticEquation(beta);
+
+    if (z <> Nil) then
+    begin
+      if (z.TestBitZero() <> (yTilde = 1)) then
+      begin
+        z := z.AddOne();
+      end;
+
+      case CoordinateSystem of
+        TECCurveConstants.COORD_LAMBDA_AFFINE,
+          TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+          begin
+            yp := z.Add(xp);
+          end
+      else
+        begin
+          yp := z.Multiply(xp);
+        end;
+      end;
+
+    end;
+
+  end;
+
+  if (yp = Nil) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidPointCompression);
+  end;
+
+  result := CreateRawPoint(xp, yp, true);
+end;
+
+destructor TAbstractF2mCurve.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TAbstractF2mCurve.GetIsKoblitz: Boolean;
+begin
+  result := (Fm_order.IsInitialized) and (Fm_cofactor.IsInitialized) and
+    (Fm_b.IsOne) and (Fm_a.IsZero or Fm_a.IsOne);
+end;
+
+function TAbstractF2mCurve.GetSi: TCryptoLibGenericArray<TBigInteger>;
+begin
+  if (Fsi = Nil) then
+  begin
+
+    FLock.Acquire;
+    try
+      if (Fsi = Nil) then
+      begin
+        Fsi := TTnaf.GetSi(Self as IAbstractF2mCurve);
+      end;
+    finally
+      FLock.Release;
+    end;
+  end;
+  result := Fsi;
+end;
+
+class function TAbstractF2mCurve.Inverse(m: Int32;
+  const ks: TCryptoLibInt32Array; const x: TBigInteger): TBigInteger;
+begin
+  result := TLongArray.Create(x).ModInverse(m, ks).ToBigInteger();
+end;
+
+function TAbstractF2mCurve.IsValidFieldElement(const x: TBigInteger): Boolean;
+begin
+  result := (x.IsInitialized) and (x.SignValue >= 0) and
+    (x.BitLength <= FieldSize);
+end;
+
+function TAbstractF2mCurve.SolveQuadraticEquation(const beta: IECFieldElement)
+  : IECFieldElement;
+var
+  gamma, z, zeroElement, t, w, w2: IECFieldElement;
+  m, i: Int32;
+begin
+  if (beta.IsZero) then
+  begin
+    result := beta;
+    Exit;
+  end;
+
+  zeroElement := FromBigInteger(TBigInteger.Zero);
+
+  m := FieldSize;
+
+  repeat
+    t := FromBigInteger(TBigInteger.Arbitrary(m));
+    z := zeroElement;
+    w := beta;
+    i := 1;
+    while i < m do
+    begin
+      w2 := w.Square();
+      z := z.Square().Add(w2.Multiply(t));
+      w := w2.Add(beta);
+      System.Inc(i);
+    end;
+
+    if (not w.IsZero) then
+    begin
+      result := Nil;
+      Exit;
+    end;
+    gamma := z.Square().Add(z);
+  until (not(gamma.IsZero));
+
+  result := z;
+end;
+
+{ TF2mCurve }
+
+function TF2mCurve.GetFieldSize: Int32;
+begin
+  result := Fm;
+end;
+
+function TF2mCurve.GetInfinity: IECPoint;
+begin
+  result := Fm_infinity;
+end;
+
+function TF2mCurve.GetK1: Int32;
+begin
+  result := Fk1;
+end;
+
+function TF2mCurve.GetK2: Int32;
+begin
+  result := Fk2;
+end;
+
+function TF2mCurve.GetK3: Int32;
+begin
+  result := Fk3;
+end;
+
+function TF2mCurve.GetM: Int32;
+begin
+  result := Fm;
+end;
+
+function TF2mCurve.IsTrinomial: Boolean;
+begin
+  result := (k2 = 0) and (k3 = 0);
+end;
+
+function TF2mCurve.CloneCurve: IECCurve;
+begin
+  result := TF2mCurve.Create(m, k1, k2, k3, Fm_a, Fm_b, Fm_order, Fm_cofactor);
+end;
+
+constructor TF2mCurve.Create(m, K: Int32; const a, b: TBigInteger);
+begin
+  Create(m, K, 0, 0, a, b, Default (TBigInteger), Default (TBigInteger));
+end;
+
+constructor TF2mCurve.Create(m, k1, k2, k3: Int32; const a, b: IECFieldElement;
+  const Order, Cofactor: TBigInteger);
+begin
+  Inherited Create(m, k1, k2, k3);
+  Fm := m;
+  Fk1 := k1;
+  Fk2 := k2;
+  Fk3 := k3;
+  Fm_order := Order;
+  Fm_cofactor := Cofactor;
+
+  Fm_infinity := TF2mPoint.Create(Self as IECCurve, Nil, Nil, false);
+  Fm_a := a;
+  Fm_b := b;
+  Fm_coord := F2M_DEFAULT_COORDS;
+end;
+
+constructor TF2mCurve.Create(m, K: Int32;
+  const a, b, Order, Cofactor: TBigInteger);
+begin
+  Create(m, K, 0, 0, a, b, Order, Cofactor);
+end;
+
+constructor TF2mCurve.Create(m, k1, k2, k3: Int32;
+  const a, b, Order, Cofactor: TBigInteger);
+begin
+  Inherited Create(m, k1, k2, k3);
+  Fm := m;
+  Fk1 := k1;
+  Fk2 := k2;
+  Fk3 := k3;
+  Fm_order := Order;
+  Fm_cofactor := Cofactor;
+  Fm_infinity := TF2mPoint.Create(Self as IECCurve, Nil, Nil, false);
+
+  if (k1 = 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidK1);
+  end;
+
+  if (k2 = 0) then
+  begin
+    if (k3 <> 0) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SInvalidK3);
+    end;
+  end
+  else
+  begin
+    if (k2 <= k1) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SK2K1MisMatch);
+    end;
+
+    if (k3 <= k2) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SK3K2Mismatch);
+    end;
+  end;
+
+  Fm_a := FromBigInteger(a);
+  Fm_b := FromBigInteger(b);
+  Fm_coord := F2M_DEFAULT_COORDS;
+
+end;
+
+function TF2mCurve.CreateCacheSafeLookupTable(const points
+  : TCryptoLibGenericArray<IECPoint>; off, len: Int32): IECLookupTable;
+var
+  FE_LONGS, position, i: Int32;
+  table: TCryptoLibInt64Array;
+  P: IECPoint;
+begin
+  FE_LONGS := (m + 63) div 64;
+  System.SetLength(table, len * FE_LONGS * 2);
+
+  position := 0;
+
+  for i := 0 to System.Pred(len) do
+  begin
+    P := points[off + i];
+    (P.RawXCoord as IF2mFieldElement).x.CopyTo(table, position);
+    position := position + FE_LONGS;
+    (P.RawYCoord as IF2mFieldElement).x.CopyTo(table, position);
+    position := position + FE_LONGS;
+  end;
+
+  result := TDefaultF2mLookupTable.Create(Self as IF2mCurve, table, len);
+end;
+
+constructor TF2mCurve.Create(m, k1, k2, k3: Int32; const a, b: TBigInteger);
+begin
+  Create(m, k1, k2, k3, a, b, Default (TBigInteger), Default (TBigInteger));
+end;
+
+function TF2mCurve.CreateDefaultMultiplier: IECMultiplier;
+begin
+  if (IsKoblitz) then
+  begin
+    result := TWTauNafMultiplier.Create();
+    Exit;
+  end;
+
+  result := (Inherited CreateDefaultMultiplier());
+end;
+
+function TF2mCurve.CreateRawPoint(const x, y: IECFieldElement;
+  withCompression: Boolean): IECPoint;
+begin
+  result := TF2mPoint.Create(Self as IECCurve, x, y, withCompression);
+end;
+
+function TF2mCurve.CreateRawPoint(const x, y: IECFieldElement;
+  const zs: TCryptoLibGenericArray<IECFieldElement>; withCompression: Boolean)
+  : IECPoint;
+begin
+  result := TF2mPoint.Create(Self as IECCurve, x, y, zs, withCompression);
+end;
+
+destructor TF2mCurve.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TF2mCurve.FromBigInteger(const x: TBigInteger): IECFieldElement;
+begin
+  result := TF2mFieldElement.Create(Fm, Fk1, Fk2, Fk3, x);
+end;
+
+function TF2mCurve.SupportsCoordinateSystem(coord: Int32): Boolean;
+begin
+  case coord of
+    TECCurveConstants.COORD_AFFINE, TECCurveConstants.COORD_HOMOGENEOUS,
+      TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        result := true;
+      end
+  else
+    begin
+      result := false;
+    end;
+  end;
+end;
+
+{ TDefaultLookupTable }
+
+constructor TDefaultLookupTable.Create(const outer: IECCurve;
+  const table: TCryptoLibByteArray; size: Int32);
+begin
+  Inherited Create();
+  Fm_outer := outer;
+  Fm_table := table;
+  Fm_size := size;
+end;
+
+function TDefaultLookupTable.GetSize: Int32;
+begin
+  result := Fm_size;
+end;
+
+function TDefaultLookupTable.Lookup(index: Int32): IECPoint;
+var
+  FE_BYTES, position, i, j: Int32;
+  x, y: TCryptoLibByteArray;
+  MASK: Byte;
+  XFieldElement, YFieldElement: IECFieldElement;
+begin
+  FE_BYTES := (Fm_outer.FieldSize + 7) div 8;
+  System.SetLength(x, FE_BYTES);
+  System.SetLength(y, FE_BYTES);
+
+  position := 0;
+
+  for i := 0 to System.Pred(Fm_size) do
+  begin
+
+    MASK := Byte(TBits.Asr32((i xor index) - 1, 31));
+
+    for j := 0 to System.Pred(FE_BYTES) do
+    begin
+
+      x[j] := x[j] xor Byte(Fm_table[position + j] and MASK);
+      y[j] := y[j] xor Byte(Fm_table[position + FE_BYTES + j] and MASK);
+    end;
+    position := position + (FE_BYTES * 2);
+  end;
+
+  XFieldElement := Fm_outer.FromBigInteger(TBigInteger.Create(1, x));
+  YFieldElement := Fm_outer.FromBigInteger(TBigInteger.Create(1, y));
+  result := Fm_outer.CreateRawPoint(XFieldElement, YFieldElement, false);
+end;
+
+{ TDefaultF2mLookupTable }
+
+constructor TDefaultF2mLookupTable.Create(const outer: IF2mCurve;
+  const table: TCryptoLibInt64Array; size: Int32);
+begin
+  Inherited Create();
+  Fm_outer := outer;
+  Fm_table := table;
+  Fm_size := size;
+end;
+
+function TDefaultF2mLookupTable.GetSize: Int32;
+begin
+  result := Fm_size;
+end;
+
+function TDefaultF2mLookupTable.Lookup(index: Int32): IECPoint;
+var
+  FE_LONGS, position, m, i, j: Int32;
+  ks: TCryptoLibInt32Array;
+  x, y: TCryptoLibInt64Array;
+  MASK: Int64;
+  XFieldElement, YFieldElement: IECFieldElement;
+begin
+  m := Fm_outer.m;
+  if Fm_outer.IsTrinomial() then
+  begin
+    ks := TCryptoLibInt32Array.Create(Fm_outer.k1);
+  end
+  else
+  begin
+    ks := TCryptoLibInt32Array.Create(Fm_outer.k1, Fm_outer.k2, Fm_outer.k3);
+  end;
+
+  FE_LONGS := (Fm_outer.m + 63) div 64;
+  System.SetLength(x, FE_LONGS);
+  System.SetLength(y, FE_LONGS);
+
+  position := 0;
+
+  for i := 0 to System.Pred(Fm_size) do
+  begin
+
+    MASK := TBits.Asr32((i xor index) - 1, 31);
+
+    for j := 0 to System.Pred(FE_LONGS) do
+    begin
+
+      x[j] := x[j] xor (Fm_table[position + j] and MASK);
+      y[j] := y[j] xor (Fm_table[position + FE_LONGS + j] and MASK);
+    end;
+    position := position + (FE_LONGS * 2);
+  end;
+
+  XFieldElement := TF2mFieldElement.Create(m, ks, TLongArray.Create(x));
+  YFieldElement := TF2mFieldElement.Create(m, ks, TLongArray.Create(y));
+  result := Fm_outer.CreateRawPoint(XFieldElement, YFieldElement, false);
+end;
+
+{ TECPoint }
+
+function TECPoint.GetIsCompressed: Boolean;
+begin
+  result := Fm_withCompression;
+end;
+
+function TECPoint.GetIsInfinity: Boolean;
+begin
+  // result := (Fm_x = Nil) and (Fm_y = Nil);
+  result := (Fm_x = Nil) or (Fm_y = Nil) or
+    ((System.Length(Fm_zs) > 0) and (Fm_zs[0].IsZero));
+end;
+
+function TECPoint.RawXCoord: IECFieldElement;
+begin
+  result := Fm_x;
+end;
+
+function TECPoint.RawYCoord: IECFieldElement;
+begin
+  result := Fm_y;
+end;
+
+function TECPoint.RawZCoords: TCryptoLibGenericArray<IECFieldElement>;
+begin
+  result := Fm_zs;
+end;
+
+function TECPoint.Normalize: IECPoint;
+var
+  Z1: IECFieldElement;
+begin
+  if (IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  case CurveCoordinateSystem of
+    TECCurveConstants.COORD_AFFINE, TECCurveConstants.COORD_LAMBDA_AFFINE:
+      begin
+        result := Self;
+        Exit;
+      end
+  else
+    begin
+
+      Z1 := RawZCoords[0];
+      if (Z1.IsOne) then
+      begin
+        result := Self;
+        Exit;
+      end;
+
+      result := Normalize(Z1.Invert());
+    end;
+  end;
+end;
+
+function TECPoint.SatisfiesOrder: Boolean;
+var
+  n: TBigInteger;
+begin
+  if (TBigInteger.One.Equals(curve.GetCofactor())) then
+  begin
+    result := true;
+    Exit;
+  end;
+
+  n := curve.GetOrder();
+
+  // TODO Require order to be available for all curves
+
+  result := (not(n.IsInitialized)) or TECAlgorithms.ReferenceMultiply
+    (Self as IECPoint, n).IsInfinity;
+end;
+
+function TECPoint.ScaleX(const scale: IECFieldElement): IECPoint;
+begin
+  if IsInfinity then
+  begin
+    result := Self;
+  end
+  else
+  begin
+    result := curve.CreateRawPoint(RawXCoord.Multiply(scale), RawYCoord,
+      RawZCoords, IsCompressed);
+  end;
+end;
+
+function TECPoint.ScaleY(const scale: IECFieldElement): IECPoint;
+begin
+  if IsInfinity then
+  begin
+    result := Self;
+  end
+  else
+  begin
+    result := curve.CreateRawPoint(RawXCoord, RawYCoord.Multiply(scale),
+      RawZCoords, IsCompressed);
+  end;
+end;
+
+procedure TECPoint.SetpreCompTable(const Value
+  : TDictionary<String, IPreCompInfo>);
+begin
+  Fm_preCompTable := Value;
+end;
+
+function TECPoint.ThreeTimes: IECPoint;
+begin
+  result := TwicePlus(Self);
+end;
+
+function TECPoint.TimesPow2(e: Int32): IECPoint;
+var
+  P: IECPoint;
+begin
+  if (e < 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SCannotBeNegative);
+  end;
+
+  P := Self;
+  System.Dec(e);
+  while (e >= 0) do
+  begin
+    P := P.Twice();
+    System.Dec(e);
+  end;
+  result := P;
+end;
+
+function TECPoint.ToString: String;
+var
+  sl: TStringList;
+  i: Int32;
+begin
+  if (IsInfinity) then
+  begin
+    result := 'INF';
+    Exit;
+  end;
+
+  sl := TStringList.Create();
+  sl.LineBreak := '';
+  try
+    sl.Add('(');
+    sl.Add(RawXCoord.ToString);
+    sl.Add(',');
+    sl.Add(RawYCoord.ToString);
+    for i := 0 to System.Pred(System.Length(Fm_zs)) do
+    begin
+      sl.Add(',');
+      sl.Add(Fm_zs[i].ToString);
+    end;
+    sl.Add(')');
+    result := sl.Text;
+  finally
+    sl.Free;
+  end;
+end;
+
+function TECPoint.TwicePlus(const b: IECPoint): IECPoint;
+begin
+  result := Twice().Add(b);
+end;
+
+constructor TECPoint.Create(const curve: IECCurve; const x, y: IECFieldElement;
+  withCompression: Boolean);
+begin
+  Create(curve, x, y, GetInitialZCoords(curve), withCompression);
+end;
+
+procedure TECPoint.CheckNormalized;
+begin
+  if (not IsNormalized()) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes(@SPointNotInNormalForm);
+  end;
+end;
+
+constructor TECPoint.Create(const curve: IECCurve; const x, y: IECFieldElement;
+  const zs: TCryptoLibGenericArray<IECFieldElement>; withCompression: Boolean);
+begin
+  Inherited Create();
+  // Fm_curve := curve;
+  TSetWeakRef.SetWeakReference(@Fm_curve, curve);
+  Fm_x := x;
+  Fm_y := y;
+  Fm_zs := zs;
+  Fm_withCompression := withCompression;
+end;
+
+function TECPoint.CreateScaledPoint(const sx, sy: IECFieldElement): IECPoint;
+begin
+  result := curve.CreateRawPoint(RawXCoord.Multiply(sx), RawYCoord.Multiply(sy),
+    IsCompressed);
+end;
+
+destructor TECPoint.Destroy;
+begin
+  TSetWeakRef.SetWeakReference(@Fm_curve, Nil);
+  Fm_preCompTable.Free;
+  inherited Destroy;
+end;
+
+class constructor TECPoint.ECPoint;
+begin
+  System.SetLength(FEMPTY_ZS, 0);
+end;
+
+function TECPoint.Equals(const other: IECPoint): Boolean;
+var
+  c1, c2: IECCurve;
+  n1, n2, i1, i2: Boolean;
+  p1, p2: IECPoint;
+  points: TCryptoLibGenericArray<IECPoint>;
+begin
+  if ((Self as IECPoint) = other) then
+  begin
+    result := true;
+    Exit;
+  end;
+  if (other = Nil) then
+  begin
+    result := false;
+    Exit;
+  end;
+
+  c1 := Self.curve;
+  c2 := other.curve;
+  n1 := (c1 = Nil);
+  n2 := (c2 = Nil);
+  i1 := IsInfinity;
+  i2 := other.IsInfinity;
+
+  if (i1 or i2) then
+  begin
+    result := (i1 and i2) and (n1 or n2 or c1.Equals(c2));
+    Exit;
+  end;
+
+  p1 := Self as IECPoint;
+  p2 := other;
+  if (n1 and n2) then
+  begin
+    // Points with null curve are in affine form, so already normalized
+  end
+  else if (n1) then
+  begin
+    p2 := p2.Normalize();
+  end
+  else if (n2) then
+  begin
+    p1 := p1.Normalize();
+  end
+  else if (not c1.Equals(c2)) then
+  begin
+    result := false;
+    Exit;
+  end
+  else
+  begin
+    // TODO Consider just requiring already normalized, to avoid silent performance degradation
+
+    points := TCryptoLibGenericArray<IECPoint>.Create(Self, c1.ImportPoint(p2));
+
+    // TODO This is a little strong, really only requires coZNormalizeAll to get Zs equal
+    c1.NormalizeAll(points);
+
+    p1 := points[0];
+    p2 := points[1];
+  end;
+
+  result := p1.XCoord.Equals(p2.XCoord) and p1.YCoord.Equals(p2.YCoord);
+end;
+
+function TECPoint.GetEncoded: TCryptoLibByteArray;
+begin
+  result := GetEncoded(Fm_withCompression);
+end;
+
+function TECPoint.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+
+var
+  c: IECCurve;
+  P: IECPoint;
+  hc: Int32;
+begin
+  c := curve;
+  if c = Nil then
+  begin
+    hc := 0;
+  end
+  else
+  begin
+    hc := not c.GetHashCode();
+  end;
+
+  if (not IsInfinity) then
+  begin
+    // TODO Consider just requiring already normalized, to avoid silent performance degradation
+
+    P := Normalize();
+
+    hc := hc xor (P.XCoord.GetHashCode() * 17);
+    hc := hc xor (P.YCoord.GetHashCode() * 257);
+  end;
+
+  result := hc;
+end;
+
+class function TECPoint.GetInitialZCoords(const curve: IECCurve)
+  : TCryptoLibGenericArray<IECFieldElement>;
+var
+  coord: Int32;
+  One: IECFieldElement;
+begin
+  // Cope with null curve, most commonly used by implicitlyCa
+  if curve = Nil then
+  begin
+    coord := TECCurveConstants.COORD_AFFINE;
+  end
+  else
+  begin
+    coord := curve.CoordinateSystem;
+  end;
+
+  case coord of
+    TECCurveConstants.COORD_AFFINE, TECCurveConstants.COORD_LAMBDA_AFFINE:
+      begin
+        result := FEMPTY_ZS;
+        Exit;
+      end;
+  end;
+
+  One := curve.FromBigInteger(TBigInteger.One);
+
+  case coord of
+
+    TECCurveConstants.COORD_HOMOGENEOUS, TECCurveConstants.COORD_JACOBIAN,
+      TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        result := TCryptoLibGenericArray<IECFieldElement>.Create(One);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_JACOBIAN_CHUDNOVSKY:
+      begin
+        result := TCryptoLibGenericArray<IECFieldElement>.Create(One, One, One);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+      begin
+        result := TCryptoLibGenericArray<IECFieldElement>.Create(One, curve.a);
+        Exit;
+      end
+
+  else
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SUnknownCoordSystem);
+    end;
+
+  end;
+
+end;
+
+function TECPoint.GetpreCompTable: TDictionary<String, IPreCompInfo>;
+begin
+  result := Fm_preCompTable;
+end;
+
+function TECPoint.GetXCoord: IECFieldElement;
+begin
+  result := Fm_x;
+end;
+
+function TECPoint.GetYCoord: IECFieldElement;
+begin
+  result := Fm_y;
+end;
+
+function TECPoint.GetZCoord(index: Int32): IECFieldElement;
+begin
+  if ((index < 0) or (index >= System.Length(Fm_zs))) then
+  begin
+    result := Nil;
+  end
+  else
+  begin
+    result := Fm_zs[index];
+  end;
+end;
+
+function TECPoint.GetZCoords: TCryptoLibGenericArray<IECFieldElement>;
+var
+  zsLen: Int32;
+begin
+  zsLen := System.Length(Fm_zs);
+  if (zsLen = 0) then
+  begin
+    result := Fm_zs;
+    Exit;
+  end;
+  System.SetLength(result, zsLen);
+  result := System.Copy(Fm_zs, 0, zsLen);
+end;
+
+function TECPoint.ImplIsValid(decompressed, checkOrder: Boolean): Boolean;
+var
+  Validity: IValidityPrecompInfo;
+  callback: IValidityCallback;
+begin
+
+  if (IsInfinity) then
+  begin
+    result := true;
+    Exit;
+  end;
+
+  callback := TValidityCallback.Create(Self as IECPoint, decompressed,
+    checkOrder);
+  Validity := curve.Precompute(Self as IECPoint,
+    TValidityPrecompInfo.PRECOMP_NAME, callback) as IValidityPrecompInfo;
+
+  result := not(Validity.hasFailed());
+end;
+
+function TECPoint.IsNormalized: Boolean;
+var
+  coord: Int32;
+begin
+  coord := CurveCoordinateSystem;
+
+  result := (coord = TECCurveConstants.COORD_AFFINE) or
+    (coord = TECCurveConstants.COORD_LAMBDA_AFFINE) or (IsInfinity) or
+    (RawZCoords[0].IsOne);
+end;
+
+function TECPoint.IsValid: Boolean;
+begin
+  result := ImplIsValid(false, true);
+end;
+
+function TECPoint.IsValidPartial: Boolean;
+begin
+  result := ImplIsValid(false, false);
+end;
+
+function TECPoint.Normalize(const zInv: IECFieldElement): IECPoint;
+var
+  zInv2, zInv3: IECFieldElement;
+begin
+  case CurveCoordinateSystem of
+    TECCurveConstants.COORD_HOMOGENEOUS,
+      TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        result := CreateScaledPoint(zInv, zInv);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_JACOBIAN,
+      TECCurveConstants.COORD_JACOBIAN_CHUDNOVSKY,
+      TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+      begin
+        zInv2 := zInv.Square();
+        zInv3 := zInv2.Multiply(zInv);
+        result := CreateScaledPoint(zInv2, zInv3);
+        Exit;
+      end
+  else
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes
+        (@SNotProjectiveCoordSystem);
+    end;
+
+  end;
+end;
+
+function TECPoint.GetAffineXCoord: IECFieldElement;
+begin
+  CheckNormalized();
+  result := XCoord;
+end;
+
+function TECPoint.GetAffineYCoord: IECFieldElement;
+begin
+  CheckNormalized();
+  result := YCoord;
+end;
+
+function TECPoint.GetCurve: IECCurve;
+begin
+  result := Fm_curve;
+end;
+
+function TECPoint.GetCurveCoordinateSystem: Int32;
+begin
+  // Cope with null curve, most commonly used by implicitlyCa
+  if Fm_curve = Nil then
+  begin
+    result := TECCurveConstants.COORD_AFFINE;
+  end
+  else
+  begin
+    result := Fm_curve.CoordinateSystem;
+  end;
+end;
+
+function TECPoint.GetDetachedPoint: IECPoint;
+begin
+  result := Normalize().Detach();
+end;
+
+{ TF2mPoint }
+
+constructor TF2mPoint.Create(const curve: IECCurve;
+  const x, y: IECFieldElement);
+begin
+  Create(curve, x, y, false);
+end;
+
+constructor TF2mPoint.Create(const curve: IECCurve; const x, y: IECFieldElement;
+  withCompression: Boolean);
+begin
+  Inherited Create(curve, x, y, withCompression);
+  if ((x = Nil) <> (y = Nil)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SNilFieldElement);
+  end;
+
+  if (x <> Nil) then
+  begin
+    // Check if x and y are elements of the same field
+    TF2mFieldElement.CheckFieldElements(x, y);
+
+    // Check if x and a are elements of the same field
+    if (curve <> Nil) then
+    begin
+      TF2mFieldElement.CheckFieldElements(x, curve.a);
+    end;
+  end;
+end;
+
+function TF2mPoint.Add(const b: IECPoint): IECPoint;
+var
+  ecCurve: IECCurve;
+  coord: Int32;
+  x1, x2, Y1, Y2, dx, dy, L, x3, Y3, Z1, Z2, U1, V1, U2, V2, u, v, Vsq, Vcu, w,
+    a, VSqZ2, uv, Z3, L3, L1, L2, S2, S1, ABZ2, AU1, AU2, bigB: IECFieldElement;
+  Z1IsOne, Z2IsOne: Boolean;
+  P: IECPoint;
+begin
+  if (IsInfinity) then
+  begin
+    result := b;
+    Exit;
+  end;
+  if (b.IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  ecCurve := curve;
+  coord := ecCurve.CoordinateSystem;
+
+  x1 := RawXCoord;
+  x2 := b.RawXCoord;
+
+  case coord of
+    TECCurveConstants.COORD_AFFINE:
+      begin
+        Y1 := RawYCoord;
+        Y2 := b.RawYCoord;
+
+        dx := x1.Add(x2);
+        dy := Y1.Add(Y2);
+        if (dx.IsZero) then
+        begin
+          if (dy.IsZero) then
+          begin
+            result := Twice();
+            Exit;
+          end;
+
+          result := ecCurve.Infinity;
+          Exit;
+        end;
+
+        L := dy.Divide(dx);
+
+        x3 := L.Square().Add(L).Add(dx).Add(ecCurve.a);
+        Y3 := L.Multiply(x1.Add(x3)).Add(x3).Add(Y1);
+
+        result := TF2mPoint.Create(ecCurve, x3, Y3, IsCompressed);
+        Exit;
+      end;
+    TECCurveConstants.COORD_HOMOGENEOUS:
+      begin
+        Y1 := RawYCoord;
+        Z1 := RawZCoords[0];
+        Y2 := b.RawYCoord;
+        Z2 := b.RawZCoords[0];
+
+        Z1IsOne := Z1.IsOne;
+        U1 := Y2;
+        V1 := x2;
+        if (not Z1IsOne) then
+        begin
+          U1 := U1.Multiply(Z1);
+          V1 := V1.Multiply(Z1);
+        end;
+
+        Z2IsOne := Z2.IsOne;
+        U2 := Y1;
+        V2 := x1;
+        if (not Z2IsOne) then
+        begin
+          U2 := U2.Multiply(Z2);
+          V2 := V2.Multiply(Z2);
+        end;
+
+        u := U1.Add(U2);
+        v := V1.Add(V2);
+
+        if (v.IsZero) then
+        begin
+          if (u.IsZero) then
+          begin
+            result := Twice();
+            Exit;
+          end;
+
+          result := ecCurve.Infinity;
+          Exit;
+        end;
+
+        Vsq := v.Square();
+        Vcu := Vsq.Multiply(v);
+
+        if Z1IsOne then
+        begin
+          w := Z2;
+        end
+        else if Z2IsOne then
+        begin
+          w := Z1;
+        end
+        else
+        begin
+          w := Z1.Multiply(Z2);
+        end;
+
+        uv := u.Add(v);
+        a := uv.MultiplyPlusProduct(u, Vsq, ecCurve.a).Multiply(w).Add(Vcu);
+
+        x3 := v.Multiply(a);
+        if Z2IsOne then
+        begin
+          VSqZ2 := Vsq;
+        end
+        else
+        begin
+          VSqZ2 := Vsq.Multiply(Z2);
+        end;
+
+        Y3 := u.MultiplyPlusProduct(x1, v, Y1).MultiplyPlusProduct
+          (VSqZ2, uv, a);
+        Z3 := Vcu.Multiply(w);
+
+        result := TF2mPoint.Create(ecCurve, x3, Y3,
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z3), IsCompressed);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        if (x1.IsZero) then
+        begin
+          if (x2.IsZero) then
+          begin
+            result := ecCurve.Infinity;
+            Exit;
+          end;
+
+          result := b.Add(Self);
+          Exit;
+        end;
+
+        L1 := RawYCoord;
+        Z1 := RawZCoords[0];
+        L2 := b.RawYCoord;
+        Z2 := b.RawZCoords[0];
+
+        Z1IsOne := Z1.IsOne;
+        U2 := x2;
+        S2 := L2;
+        if (not Z1IsOne) then
+        begin
+          U2 := U2.Multiply(Z1);
+          S2 := S2.Multiply(Z1);
+        end;
+
+        Z2IsOne := Z2.IsOne;
+        U1 := x1;
+        S1 := L1;
+        if (not Z2IsOne) then
+        begin
+          U1 := U1.Multiply(Z2);
+          S1 := S1.Multiply(Z2);
+        end;
+
+        a := S1.Add(S2);
+        bigB := U1.Add(U2);
+
+        if (bigB.IsZero) then
+        begin
+          if (a.IsZero) then
+          begin
+            result := Twice();
+            Exit;
+          end;
+
+          result := ecCurve.Infinity;
+          Exit;
+        end;
+
+        if (x2.IsZero) then
+        begin
+          // TODO This can probably be optimized quite a bit
+          P := Normalize();
+          x1 := P.RawXCoord;
+          Y1 := P.YCoord;
+
+          Y2 := L2;
+          L := Y1.Add(Y2).Divide(x1);
+
+          x3 := L.Square().Add(L).Add(x1).Add(ecCurve.a);
+          if (x3.IsZero) then
+          begin
+            result := TF2mPoint.Create(ecCurve, x3, ecCurve.b.Sqrt(),
+              IsCompressed);
+            Exit;
+          end;
+
+          Y3 := L.Multiply(x1.Add(x3)).Add(x3).Add(Y1);
+          L3 := Y3.Divide(x3).Add(x3);
+          Z3 := ecCurve.FromBigInteger(TBigInteger.One);
+        end
+        else
+        begin
+          bigB := bigB.Square();
+
+          AU1 := a.Multiply(U1);
+          AU2 := a.Multiply(U2);
+
+          x3 := AU1.Multiply(AU2);
+          if (x3.IsZero) then
+          begin
+            result := TF2mPoint.Create(ecCurve, x3, ecCurve.b.Sqrt(),
+              IsCompressed);
+            Exit;
+          end;
+
+          ABZ2 := a.Multiply(bigB);
+          if (not Z2IsOne) then
+          begin
+            ABZ2 := ABZ2.Multiply(Z2);
+          end;
+
+          L3 := AU2.Add(bigB).SquarePlusProduct(ABZ2, L1.Add(Z1));
+
+          Z3 := ABZ2;
+          if (not Z1IsOne) then
+          begin
+            Z3 := Z3.Multiply(Z1);
+          end;
+        end;
+
+        result := TF2mPoint.Create(ecCurve, x3, L3,
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z3), IsCompressed);
+        Exit;
+      end
+  else
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes
+        (@SUnSupportedCoordinateSystem);
+    end;
+
+  end;
+
+end;
+
+constructor TF2mPoint.Create(const curve: IECCurve; const x, y: IECFieldElement;
+  const zs: TCryptoLibGenericArray<IECFieldElement>; withCompression: Boolean);
+begin
+  Inherited Create(curve, x, y, zs, withCompression);
+end;
+
+destructor TF2mPoint.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TF2mPoint.Detach: IECPoint;
+begin
+  result := TF2mPoint.Create(Nil, AffineXCoord, AffineYCoord, false);
+end;
+
+function TF2mPoint.GetCompressionYTilde: Boolean;
+var
+  Lx, LY: IECFieldElement;
+begin
+  Lx := RawXCoord;
+  if (Lx.IsZero) then
+  begin
+    result := false;
+    Exit;
+  end;
+
+  LY := RawYCoord;
+
+  case CurveCoordinateSystem of
+    TECCurveConstants.COORD_LAMBDA_AFFINE,
+      TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        // Y is actually Lambda (X + Y/X) here
+        result := LY.TestBitZero() <> Lx.TestBitZero();
+        Exit;
+      end
+  else
+    begin
+      result := LY.Divide(Lx).TestBitZero();
+    end;
+  end;
+
+end;
+
+function TF2mPoint.GetYCoord: IECFieldElement;
+var
+  coord: Int32;
+  Lx, L, LY, z: IECFieldElement;
+begin
+  coord := CurveCoordinateSystem;
+
+  case coord of
+    TECCurveConstants.COORD_LAMBDA_AFFINE,
+      TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        Lx := RawXCoord;
+        L := RawYCoord;
+
+        if (IsInfinity or Lx.IsZero) then
+        begin
+          result := L;
+          Exit;
+        end;
+
+        // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+        LY := L.Add(Lx).Multiply(Lx);
+        if (TECCurveConstants.COORD_LAMBDA_PROJECTIVE = coord) then
+        begin
+          z := RawZCoords[0];
+          if (not z.IsOne) then
+          begin
+            LY := LY.Divide(z);
+          end;
+        end;
+        result := LY;
+        Exit;
+      end
+  else
+    begin
+      result := RawYCoord;
+    end;
+  end;
+
+end;
+
+function TF2mPoint.Negate: IECPoint;
+var
+  Lx, LY, bigY, z, L: IECFieldElement;
+  ecCurve: IECCurve;
+  coord: Int32;
+begin
+  if (IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  Lx := RawXCoord;
+  if (Lx.IsZero) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  ecCurve := curve;
+  coord := ecCurve.CoordinateSystem;
+
+  case coord of
+    TECCurveConstants.COORD_AFFINE:
+      begin
+        bigY := RawYCoord;
+        result := TF2mPoint.Create(ecCurve, Lx, bigY.Add(Lx), IsCompressed);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_HOMOGENEOUS:
+      begin
+        LY := RawYCoord;
+        z := RawZCoords[0];
+        result := TF2mPoint.Create(ecCurve, Lx, LY.Add(Lx),
+          TCryptoLibGenericArray<IECFieldElement>.Create(z), IsCompressed);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_LAMBDA_AFFINE:
+      begin
+        L := RawYCoord;
+        result := TF2mPoint.Create(ecCurve, Lx, L.AddOne(), IsCompressed);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        // L is actually Lambda (X + Y/X) here
+        L := RawYCoord;
+        z := RawZCoords[0];
+        result := TF2mPoint.Create(ecCurve, Lx, L.Add(z),
+          TCryptoLibGenericArray<IECFieldElement>.Create(z), IsCompressed);
+        Exit;
+      end
+
+  else
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes
+        (@SUnSupportedCoordinateSystem);
+    end;
+
+  end;
+
+end;
+
+function TF2mPoint.Twice: IECPoint;
+var
+  ecCurve: IECCurve;
+  x1, Y1, L1, x3, Y3, Z1, X1Z1, X1Sq, Y1Z1, s, v, vSquared, sv, h, Z3, L1Z1,
+    Z1Sq, a, aZ1Sq, L3, t, b, t1, t2: IECFieldElement;
+  coord: Int32;
+  Z1IsOne: Boolean;
+begin
+  if (IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  ecCurve := curve;
+
+  x1 := RawXCoord;
+  if (x1.IsZero) then
+  begin
+    // A point with X == 0 is it's own additive inverse
+    result := ecCurve.Infinity;
+    Exit;
+  end;
+
+  coord := ecCurve.CoordinateSystem;
+  case coord of
+    TECCurveConstants.COORD_AFFINE:
+      begin
+        Y1 := RawYCoord;
+
+        L1 := Y1.Divide(x1).Add(x1);
+
+        x3 := L1.Square().Add(L1).Add(ecCurve.a);
+        Y3 := x1.SquarePlusProduct(x3, L1.AddOne());
+
+        result := TF2mPoint.Create(ecCurve, x3, Y3, IsCompressed);
+        Exit;
+      end;
+    TECCurveConstants.COORD_HOMOGENEOUS:
+      begin
+        Y1 := RawYCoord;
+        Z1 := RawZCoords[0];
+
+        Z1IsOne := Z1.IsOne;
+
+        if Z1IsOne then
+        begin
+          X1Z1 := x1;
+        end
+        else
+        begin
+          X1Z1 := x1.Multiply(Z1);
+        end;
+
+        if Z1IsOne then
+        begin
+          Y1Z1 := Y1;
+        end
+        else
+        begin
+          Y1Z1 := Y1.Multiply(Z1);
+        end;
+
+        X1Sq := x1.Square();
+        s := X1Sq.Add(Y1Z1);
+        v := X1Z1;
+        vSquared := v.Square();
+        sv := s.Add(v);
+        h := sv.MultiplyPlusProduct(s, vSquared, ecCurve.a);
+
+        x3 := v.Multiply(h);
+        Y3 := X1Sq.Square().MultiplyPlusProduct(v, h, sv);
+        Z3 := v.Multiply(vSquared);
+
+        result := TF2mPoint.Create(ecCurve, x3, Y3,
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z3), IsCompressed);
+        Exit;
+      end;
+    TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        L1 := RawYCoord;
+        Z1 := RawZCoords[0];
+
+        Z1IsOne := Z1.IsOne;
+        if Z1IsOne then
+        begin
+          L1Z1 := L1;
+        end
+        else
+        begin
+          L1Z1 := L1.Multiply(Z1);
+        end;
+
+        if Z1IsOne then
+        begin
+          Z1Sq := Z1;
+        end
+        else
+        begin
+          Z1Sq := Z1.Square();
+        end;
+
+        a := ecCurve.a;
+
+        if Z1IsOne then
+        begin
+          aZ1Sq := a;
+        end
+        else
+        begin
+          aZ1Sq := a.Multiply(Z1Sq);
+        end;
+
+        t := L1.Square().Add(L1Z1).Add(aZ1Sq);
+        if (t.IsZero) then
+        begin
+          result := TF2mPoint.Create(ecCurve, t, ecCurve.b.Sqrt(),
+            IsCompressed);
+          Exit;
+        end;
+
+        x3 := t.Square();
+
+        if Z1IsOne then
+        begin
+          Z3 := t;
+        end
+        else
+        begin
+          Z3 := t.Multiply(Z1Sq);
+        end;
+
+        b := ecCurve.b;
+
+        if (b.BitLength < (TBits.Asr32(ecCurve.FieldSize, 1))) then
+        begin
+          t1 := L1.Add(x1).Square();
+
+          if (b.IsOne) then
+          begin
+            t2 := aZ1Sq.Add(Z1Sq).Square();
+          end
+          else
+          begin
+            // TODO Can be calculated with one square if we pre-compute sqrt(b)
+            t2 := aZ1Sq.SquarePlusProduct(b, Z1Sq.Square());
+          end;
+          L3 := t1.Add(t).Add(Z1Sq).Multiply(t1).Add(t2).Add(x3);
+          if (a.IsZero) then
+          begin
+            L3 := L3.Add(Z3);
+          end
+          else if (not a.IsOne) then
+          begin
+            L3 := L3.Add(a.AddOne().Multiply(Z3));
+          end
+        end
+        else
+        begin
+
+          if Z1IsOne then
+          begin
+            X1Z1 := x1;
+          end
+          else
+          begin
+            X1Z1 := x1.Multiply(Z1);
+          end;
+          L3 := X1Z1.SquarePlusProduct(t, L1Z1).Add(x3).Add(Z3);
+        end;
+
+        result := TF2mPoint.Create(ecCurve, x3, L3,
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z3), IsCompressed);
+        Exit;
+      end
+  else
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes
+        (@SUnSupportedCoordinateSystem);
+    end;
+  end;
+end;
+
+function TF2mPoint.TwicePlus(const b: IECPoint): IECPoint;
+var
+  ecCurve: IECCurve;
+  x1, x2, Z2, L1, L2, Z1, X1Sq, L1Sq, Z1Sq, L1Z1, t, L2plus1, a, X2Z1Sq, bigB,
+    x3, L3, Z3: IECFieldElement;
+  coord: Int32;
+begin
+  if (IsInfinity) then
+  begin
+    result := b;
+    Exit;
+  end;
+  if (b.IsInfinity) then
+  begin
+    result := Twice();
+    Exit;
+  end;
+
+  ecCurve := curve;
+
+  x1 := RawXCoord;
+  if (x1.IsZero) then
+  begin
+    // A point with X == 0 is it's own additive inverse
+    result := b;
+    Exit;
+  end;
+
+  coord := ecCurve.CoordinateSystem;
+
+  case coord of
+    TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+
+        // NOTE: twicePlus() only optimized for lambda-affine argument
+        x2 := b.RawXCoord;
+        Z2 := b.RawZCoords[0];
+        if ((x2.IsZero) or (not Z2.IsOne)) then
+        begin
+          result := Twice().Add(b);
+          Exit;
+        end;
+
+        L1 := RawYCoord;
+        Z1 := RawZCoords[0];
+        L2 := b.RawYCoord;
+
+        X1Sq := x1.Square();
+        L1Sq := L1.Square();
+        Z1Sq := Z1.Square();
+        L1Z1 := L1.Multiply(Z1);
+
+        t := ecCurve.a.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1);
+        L2plus1 := L2.AddOne();
+        a := ecCurve.a.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq)
+          .MultiplyPlusProduct(t, X1Sq, Z1Sq);
+        X2Z1Sq := x2.Multiply(Z1Sq);
+        bigB := X2Z1Sq.Add(t).Square();
+
+        if (bigB.IsZero) then
+        begin
+          if (a.IsZero) then
+          begin
+            result := b.Twice();
+            Exit;
+          end;
+
+          result := ecCurve.Infinity;
+          Exit;
+        end;
+
+        if (a.IsZero) then
+        begin
+          result := TF2mPoint.Create(ecCurve, a, ecCurve.b.Sqrt(),
+            IsCompressed);
+          Exit;
+        end;
+
+        x3 := a.Square().Multiply(X2Z1Sq);
+        Z3 := a.Multiply(bigB).Multiply(Z1Sq);
+        L3 := a.Add(bigB).Square().MultiplyPlusProduct(t, L2plus1, Z3);
+
+        result := TF2mPoint.Create(ecCurve, x3, L3,
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z3), IsCompressed);
+        Exit;
+      end
+  else
+    begin
+      result := Twice().Add(b);
+      Exit;
+    end;
+  end;
+
+end;
+
+{ TECPointBase }
+
+constructor TECPointBase.Create(const curve: IECCurve;
+  const x, y: IECFieldElement; withCompression: Boolean);
+begin
+  Inherited Create(curve, x, y, withCompression);
+end;
+
+constructor TECPointBase.Create(const curve: IECCurve;
+  const x, y: IECFieldElement;
+  const zs: TCryptoLibGenericArray<IECFieldElement>; withCompression: Boolean);
+begin
+  Inherited Create(curve, x, y, zs, withCompression);
+end;
+
+destructor TECPointBase.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TECPointBase.GetEncoded(compressed: Boolean): TCryptoLibByteArray;
+var
+  normed: IECPoint;
+  Lx, LY, PO: TCryptoLibByteArray;
+begin
+  if (IsInfinity) then
+  begin
+    System.SetLength(result, 1);
+    Exit;
+  end;
+
+  normed := Normalize();
+
+  Lx := normed.XCoord.GetEncoded();
+
+  if (compressed) then
+  begin
+    System.SetLength(PO, System.Length(Lx) + 1);
+    if normed.CompressionYTilde then
+    begin
+      PO[0] := Byte($03);
+    end
+    else
+    begin
+      PO[0] := Byte($02);
+    end;
+
+    System.Move(Lx[0], PO[1], System.Length(Lx) * System.SizeOf(Byte));
+
+    result := PO;
+    Exit;
+  end;
+
+  LY := normed.YCoord.GetEncoded();
+
+  System.SetLength(PO, System.Length(Lx) + System.Length(LY) + 1);
+
+  PO[0] := $04;
+
+  System.Move(Lx[0], PO[1], System.Length(Lx) * System.SizeOf(Byte));
+  System.Move(LY[0], PO[System.Length(Lx) + 1],
+    System.Length(LY) * System.SizeOf(Byte));
+
+  result := PO;
+
+end;
+
+function TECPointBase.Multiply(K: TBigInteger): IECPoint;
+begin
+  result := curve.GetMultiplier().Multiply(Self as IECPoint, K);
+end;
+
+{ TAbstractFpPoint }
+
+function TAbstractFpPoint.GetCompressionYTilde: Boolean;
+begin
+  result := AffineYCoord.TestBitZero();
+end;
+
+constructor TAbstractFpPoint.Create(const curve: IECCurve;
+  const x, y: IECFieldElement; withCompression: Boolean);
+begin
+  Inherited Create(curve, x, y, withCompression);
+end;
+
+constructor TAbstractFpPoint.Create(const curve: IECCurve;
+  const x, y: IECFieldElement;
+  const zs: TCryptoLibGenericArray<IECFieldElement>; withCompression: Boolean);
+begin
+  Inherited Create(curve, x, y, zs, withCompression);
+end;
+
+destructor TAbstractFpPoint.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TAbstractFpPoint.SatisfiesCurveEquation: Boolean;
+var
+  Lx, LY, a, b, lhs, rhs, z, Z2, Z3, Z4, Z6: IECFieldElement;
+begin
+  Lx := RawXCoord;
+  LY := RawYCoord;
+  a := curve.a;
+  b := curve.b;
+  lhs := LY.Square();
+
+  case CurveCoordinateSystem of
+    TECCurveConstants.COORD_AFFINE:
+      begin
+        // do nothing
+      end;
+
+    TECCurveConstants.COORD_HOMOGENEOUS:
+      begin
+        z := RawZCoords[0];
+        if (not z.IsOne) then
+        begin
+          Z2 := z.Square();
+          Z3 := z.Multiply(Z2);
+          lhs := lhs.Multiply(z);
+          a := a.Multiply(Z2);
+          b := b.Multiply(Z3);
+        end;
+      end;
+
+    TECCurveConstants.COORD_JACOBIAN,
+      TECCurveConstants.COORD_JACOBIAN_CHUDNOVSKY,
+      TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+      begin
+        z := RawZCoords[0];
+        if (not z.IsOne) then
+        begin
+          Z2 := z.Square();
+          Z4 := Z2.Square();
+          Z6 := Z2.Multiply(Z4);
+          a := a.Multiply(Z4);
+          b := b.Multiply(Z6);
+        end;
+      end
+  else
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes
+        (@SUnSupportedCoordinateSystem);
+    end;
+
+  end;
+
+  rhs := Lx.Square().Add(a).Multiply(Lx).Add(b);
+  result := lhs.Equals(rhs);
+end;
+
+function TAbstractFpPoint.Subtract(const b: IECPoint): IECPoint;
+begin
+  if (b.IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  // Add -b
+  result := Add(b.Negate());
+end;
+
+{ TFpPoint }
+
+function TFpPoint.Add(const b: IECPoint): IECPoint;
+var
+  ecCurve: IECCurve;
+  coord: Int32;
+  gamma, x1, x2, Y1, Y2, dx, dy, x3, Y3, Z1, Z2, U1, V1, U2, V2, u, v, w, a, Z3,
+    S2, S1, vSquared, vCubed, vSquaredV2, Z1Squared, bigU2, Z3Squared, c, W1,
+    w2, A1, Z1Cubed, Z2Squared, bigU1, h, r, HSquared, G, W3,
+    Z2Cubed: IECFieldElement;
+  zs: TCryptoLibGenericArray<IECFieldElement>;
+  Z1IsOne, Z2IsOne: Boolean;
+begin
+  if (IsInfinity) then
+  begin
+    result := b;
+    Exit;
+  end;
+  if (b.IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  if (Self as IECPoint = b) then
+  begin
+    result := Twice();
+    Exit;
+  end;
+
+  ecCurve := curve;
+  coord := ecCurve.CoordinateSystem;
+
+  x1 := RawXCoord;
+  Y1 := RawYCoord;
+  x2 := b.RawXCoord;
+  Y2 := b.RawYCoord;
+
+  case coord of
+    TECCurveConstants.COORD_AFFINE:
+      begin
+
+        dx := x2.Subtract(x1);
+        dy := Y2.Subtract(Y1);
+
+        if (dx.IsZero) then
+        begin
+          if (dy.IsZero) then
+          begin
+            // this == b, i.e. this must be doubled
+            result := Twice();
+            Exit;
+          end;
+
+          // this == -b, i.e. the result is the point at infinity
+          result := curve.Infinity;
+          Exit;
+        end;
+
+        gamma := dy.Divide(dx);
+        x3 := gamma.Square().Subtract(x1).Subtract(x2);
+        Y3 := gamma.Multiply(x1.Subtract(x3)).Subtract(Y1);
+
+        result := TFpPoint.Create(curve, x3, Y3, IsCompressed);
+        Exit;
+      end;
+    TECCurveConstants.COORD_HOMOGENEOUS:
+      begin
+        Z1 := RawZCoords[0];
+        Z2 := b.RawZCoords[0];
+
+        Z1IsOne := Z1.IsOne;
+        Z2IsOne := Z2.IsOne;
+
+        if Z1IsOne then
+        begin
+          U1 := Y2;
+        end
+        else
+        begin
+          U1 := Y2.Multiply(Z1);
+        end;
+
+        if Z2IsOne then
+        begin
+          U2 := Y1;
+        end
+        else
+        begin
+          U2 := Y1.Multiply(Z2);
+        end;
+
+        u := U1.Subtract(U2);
+
+        if Z1IsOne then
+        begin
+          V1 := x2;
+        end
+        else
+        begin
+          V1 := x2.Multiply(Z1);
+        end;
+
+        if Z2IsOne then
+        begin
+          V2 := x1;
+        end
+        else
+        begin
+          V2 := x1.Multiply(Z2);
+        end;
+
+        v := V1.Subtract(V2);
+
+        // Check if b = this or b = -this
+        if (v.IsZero) then
+        begin
+          if (u.IsZero) then
+          begin
+            // this = b, i.e. this must be doubled
+            result := Twice();
+            Exit;
+          end;
+
+          // this = -b, i.e. the result is the point at infinity
+          result := ecCurve.Infinity;
+          Exit;
+        end;
+
+        // TODO Optimize for when w = 1
+        if Z1IsOne then
+        begin
+          w := Z2;
+        end
+        else if Z2IsOne then
+
+        begin
+          w := Z1;
+        end
+        else
+        begin
+          w := Z1.Multiply(Z2);
+        end;
+
+        vSquared := v.Square();
+        vCubed := vSquared.Multiply(v);
+        vSquaredV2 := vSquared.Multiply(V2);
+        a := u.Square().Multiply(w).Subtract(vCubed).Subtract(Two(vSquaredV2));
+
+        x3 := v.Multiply(a);
+        Y3 := vSquaredV2.Subtract(a).MultiplyMinusProduct(u, U2, vCubed);
+        Z3 := vCubed.Multiply(w);
+
+        result := TFpPoint.Create(ecCurve, x3, Y3,
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z3), IsCompressed);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_JACOBIAN, TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+      begin
+        Z1 := RawZCoords[0];
+        Z2 := b.RawZCoords[0];
+
+        Z1IsOne := Z1.IsOne;
+
+        x3 := Nil;
+        Y3 := Nil;
+        Z3 := Nil;
+        Z3Squared := Nil;
+
+        if ((not Z1IsOne) and (Z1.Equals(Z2))) then
+        begin
+          // TODO Make this available as public method coZAdd?
+
+          dx := x1.Subtract(x2);
+          dy := Y1.Subtract(Y2);
+          if (dx.IsZero) then
+          begin
+            if (dy.IsZero) then
+            begin
+              result := Twice();
+              Exit;
+            end;
+            result := ecCurve.Infinity;
+            Exit;
+          end;
+
+          c := dx.Square();
+          W1 := x1.Multiply(c);
+          w2 := x2.Multiply(c);
+          A1 := W1.Subtract(w2).Multiply(Y1);
+
+          x3 := dy.Square().Subtract(W1).Subtract(w2);
+          Y3 := W1.Subtract(x3).Multiply(dy).Subtract(A1);
+          Z3 := dx;
+
+          if (Z1IsOne) then
+          begin
+            Z3Squared := c;
+          end
+          else
+          begin
+            Z3 := Z3.Multiply(Z1);
+          end
+        end
+        else
+        begin
+
+          if (Z1IsOne) then
+          begin
+            Z1Squared := Z1;
+            bigU2 := x2;
+            S2 := Y2;
+          end
+          else
+          begin
+            Z1Squared := Z1.Square();
+            bigU2 := Z1Squared.Multiply(x2);
+            Z1Cubed := Z1Squared.Multiply(Z1);
+            S2 := Z1Cubed.Multiply(Y2);
+          end;
+
+          Z2IsOne := Z2.IsOne;
+
+          if (Z2IsOne) then
+          begin
+            Z2Squared := Z2;
+            bigU1 := x1;
+            S1 := Y1;
+          end
+          else
+          begin
+            Z2Squared := Z2.Square();
+            bigU1 := Z2Squared.Multiply(x1);
+            Z2Cubed := Z2Squared.Multiply(Z2);
+            S1 := Z2Cubed.Multiply(Y1);
+          end;
+
+          h := bigU1.Subtract(bigU2);
+          r := S1.Subtract(S2);
+
+          // Check if b == this or b == -this
+          if (h.IsZero) then
+          begin
+            if (r.IsZero) then
+            begin
+              // this == b, i.e. this must be doubled
+              result := Twice();
+              Exit;
+            end;
+
+            // this == -b, i.e. the result is the point at infinity
+            result := ecCurve.Infinity;
+            Exit;
+          end;
+
+          HSquared := h.Square();
+          G := HSquared.Multiply(h);
+          v := HSquared.Multiply(bigU1);
+
+          x3 := r.Square().Add(G).Subtract(Two(v));
+          Y3 := v.Subtract(x3).MultiplyMinusProduct(r, G, S1);
+
+          Z3 := h;
+          if (not Z1IsOne) then
+          begin
+            Z3 := Z3.Multiply(Z1);
+          end;
+          if (not Z2IsOne) then
+          begin
+            Z3 := Z3.Multiply(Z2);
+          end;
+
+          // Alternative calculation of Z3 using fast square
+          // X3 := four(X3);
+          // Y3 := eight(Y3);
+          // Z3 := doubleProductFromSquares(Z1, Z2, Z1Squared, Z2Squared).Multiply(H);
+
+          if (Z3 = h) then
+          begin
+            Z3Squared := HSquared;
+          end;
+        end;
+
+        if (coord = TECCurveConstants.COORD_JACOBIAN_MODIFIED) then
+        begin
+          // TODO If the result will only be used in a subsequent addition, we don't need W3
+          W3 := CalculateJacobianModifiedW(Z3, Z3Squared);
+
+          zs := TCryptoLibGenericArray<IECFieldElement>.Create(Z3, W3);
+        end
+        else
+        begin
+          zs := TCryptoLibGenericArray<IECFieldElement>.Create(Z3);
+        end;
+
+        result := TFpPoint.Create(ecCurve, x3, Y3, zs, IsCompressed);
+        Exit;
+      end
+  else
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes
+        (@SUnSupportedCoordinateSystem);
+    end;
+
+  end;
+
+end;
+
+function TFpPoint.CalculateJacobianModifiedW(const z: IECFieldElement;
+  const ZSquared: IECFieldElement): IECFieldElement;
+var
+  a4, w, a4Neg, LZSquared: IECFieldElement;
+begin
+  a4 := curve.a;
+  LZSquared := ZSquared;
+  if ((a4.IsZero) or (z.IsOne)) then
+  begin
+    result := a4;
+    Exit;
+  end;
+
+  if (LZSquared = Nil) then
+  begin
+    LZSquared := z.Square();
+  end;
+
+  w := LZSquared.Square();
+  a4Neg := a4.Negate();
+  if (a4Neg.BitLength < a4.BitLength) then
+  begin
+    w := w.Multiply(a4Neg).Negate();
+  end
+  else
+  begin
+    w := w.Multiply(a4);
+  end;
+  result := w;
+end;
+
+constructor TFpPoint.Create(const curve: IECCurve; const x, y: IECFieldElement;
+  const zs: TCryptoLibGenericArray<IECFieldElement>; withCompression: Boolean);
+begin
+  Inherited Create(curve, x, y, zs, withCompression);
+end;
+
+constructor TFpPoint.Create(const curve: IECCurve; const x, y: IECFieldElement;
+  withCompression: Boolean);
+begin
+  Inherited Create(curve, x, y, withCompression);
+  if ((x = Nil) <> (y = Nil)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SNilFieldElement);
+  end;
+end;
+
+constructor TFpPoint.Create(const curve: IECCurve; const x, y: IECFieldElement);
+begin
+  Create(curve, x, y, false);
+end;
+
+destructor TFpPoint.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TFpPoint.Detach: IECPoint;
+begin
+  result := TFpPoint.Create(Nil, AffineXCoord, AffineYCoord, false);
+end;
+
+function TFpPoint.DoubleProductFromSquares(const a, b, aSquared,
+  bSquared: IECFieldElement): IECFieldElement;
+begin
+  // /*
+  // * NOTE: If squaring in the field is faster than multiplication, then this is a quicker
+  // * way to calculate 2.A.B, if A^2 and B^2 are already known.
+  // */
+  result := a.Add(b).Square().Subtract(aSquared).Subtract(bSquared);
+end;
+
+function TFpPoint.Eight(const x: IECFieldElement): IECFieldElement;
+begin
+  result := Four(Two(x));
+end;
+
+function TFpPoint.Four(const x: IECFieldElement): IECFieldElement;
+begin
+  result := Two(Two(x));
+end;
+
+function TFpPoint.GetJacobianModifiedW: IECFieldElement;
+var
+  ZZ: TCryptoLibGenericArray<IECFieldElement>;
+  w: IECFieldElement;
+begin
+  ZZ := RawZCoords;
+  w := ZZ[1];
+  if (w = Nil) then
+  begin
+    // NOTE: Rarely, TwicePlus will result in the need for a lazy W1 calculation here
+    w := CalculateJacobianModifiedW(ZZ[0], Nil);
+    ZZ[1] := w;
+  end;
+  result := w;
+end;
+
+function TFpPoint.GetZCoord(index: Int32): IECFieldElement;
+begin
+  if ((index = 1) and (TECCurveConstants.COORD_JACOBIAN_MODIFIED =
+    CurveCoordinateSystem)) then
+  begin
+    result := GetJacobianModifiedW();
+    Exit;
+  end;
+
+  result := (Inherited GetZCoord(index));
+end;
+
+function TFpPoint.Negate: IECPoint;
+var
+  Lcurve: IECCurve;
+  coord: Int32;
+begin
+  if (IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  Lcurve := curve;
+  coord := Lcurve.CoordinateSystem;
+
+  if (TECCurveConstants.COORD_AFFINE <> coord) then
+  begin
+    result := TFpPoint.Create(Lcurve, RawXCoord, RawYCoord.Negate(), RawZCoords,
+      IsCompressed);
+    Exit;
+  end;
+
+  result := TFpPoint.Create(Lcurve, RawXCoord, RawYCoord.Negate(),
+    IsCompressed);
+end;
+
+function TFpPoint.Three(const x: IECFieldElement): IECFieldElement;
+begin
+  result := Two(x).Add(x);
+end;
+
+function TFpPoint.ThreeTimes: IECPoint;
+var
+  Y1, x1, _2Y1, Lx, z, LY, d, bigD, i, L1, L2, X4, Y4: IECFieldElement;
+  ecCurve: IECCurve;
+  coord: Int32;
+begin
+  if (IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  Y1 := RawYCoord;
+  if (Y1.IsZero) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  ecCurve := curve;
+  coord := ecCurve.CoordinateSystem;
+
+  case coord of
+    TECCurveConstants.COORD_AFFINE:
+      begin
+
+        x1 := RawXCoord;
+
+        _2Y1 := Two(Y1);
+        Lx := _2Y1.Square();
+        z := Three(x1.Square()).Add(curve.a);
+        LY := z.Square();
+
+        d := Three(x1).Multiply(Lx).Subtract(LY);
+        if (d.IsZero) then
+        begin
+          result := curve.Infinity;
+          Exit;
+        end;
+
+        bigD := d.Multiply(_2Y1);
+        i := bigD.Invert();
+        L1 := d.Multiply(i).Multiply(z);
+        L2 := Lx.Square().Multiply(i).Subtract(L1);
+
+        X4 := (L2.Subtract(L1)).Multiply(L1.Add(L2)).Add(x1);
+        Y4 := (x1.Subtract(X4)).Multiply(L2).Subtract(Y1);
+        result := TFpPoint.Create(curve, X4, Y4, IsCompressed);
+        Exit;
+      end;
+    TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+      begin
+        result := TwiceJacobianModified(false).Add(Self);
+        Exit;
+      end
+  else
+    begin
+      // NOTE: Be careful about recursions between TwicePlus and ThreeTimes
+      result := Twice().Add(Self);
+    end;
+
+  end;
+
+end;
+
+function TFpPoint.TimesPow2(e: Int32): IECPoint;
+var
+  ecCurve: IECCurve;
+  Y1, W1, x1, Z1, Z1Sq, X1Squared, m, _2Y1, _2Y1Squared, s, _4T, _8T, zInv,
+    zInv2, zInv3: IECFieldElement;
+  coord, i: Int32;
+begin
+  if (e < 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SCannotBeNegative);
+  end;
+  if ((e = 0) or (IsInfinity)) then
+  begin
+    result := Self;
+    Exit;
+  end;
+  if (e = 1) then
+  begin
+    result := Twice();
+    Exit;
+  end;
+
+  ecCurve := curve;
+
+  Y1 := RawYCoord;
+  if (Y1.IsZero) then
+  begin
+    result := ecCurve.Infinity;
+    Exit;
+  end;
+
+  coord := ecCurve.CoordinateSystem;
+
+  W1 := ecCurve.a;
+  x1 := RawXCoord;
+  if RawZCoords = Nil then
+  begin
+    Z1 := ecCurve.FromBigInteger(TBigInteger.One);
+  end
+  else
+  begin
+    Z1 := RawZCoords[0];
+  end;
+
+  if (not Z1.IsOne) then
+  begin
+    case coord of
+      TECCurveConstants.COORD_HOMOGENEOUS:
+        begin
+          Z1Sq := Z1.Square();
+          x1 := x1.Multiply(Z1);
+          Y1 := Y1.Multiply(Z1Sq);
+          W1 := CalculateJacobianModifiedW(Z1, Z1Sq);
+        end;
+      TECCurveConstants.COORD_JACOBIAN:
+        begin
+          W1 := CalculateJacobianModifiedW(Z1, Nil);
+        end;
+
+      TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+        begin
+          W1 := GetJacobianModifiedW();
+        end;
+    end;
+
+  end;
+
+  i := 0;
+  while i < e do
+  begin
+    if (Y1.IsZero) then
+    begin
+      result := ecCurve.Infinity;
+      Exit;
+    end;
+
+    X1Squared := x1.Square();
+    m := Three(X1Squared);
+    _2Y1 := Two(Y1);
+    _2Y1Squared := _2Y1.Multiply(Y1);
+    s := Two(x1.Multiply(_2Y1Squared));
+    _4T := _2Y1Squared.Square();
+    _8T := Two(_4T);
+
+    if (not W1.IsZero) then
+    begin
+      m := m.Add(W1);
+      W1 := Two(_8T.Multiply(W1));
+    end;
+
+    x1 := m.Square().Subtract(Two(s));
+    Y1 := m.Multiply(s.Subtract(x1)).Subtract(_8T);
+    if Z1.IsOne then
+    begin
+      Z1 := _2Y1;
+    end
+    else
+    begin
+      Z1 := _2Y1.Multiply(Z1);
+    end;
+
+    System.Inc(i);
+  end;
+
+  case coord of
+    TECCurveConstants.COORD_AFFINE:
+      begin
+        zInv := Z1.Invert();
+        zInv2 := zInv.Square();
+        zInv3 := zInv2.Multiply(zInv);
+
+        result := TFpPoint.Create(ecCurve, x1.Multiply(zInv2),
+          Y1.Multiply(zInv3), IsCompressed);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_HOMOGENEOUS:
+      begin
+        x1 := x1.Multiply(Z1);
+        Z1 := Z1.Multiply(Z1.Square());
+        result := TFpPoint.Create(ecCurve, x1, Y1,
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z1), IsCompressed);
+        Exit;
+      end;
+    TECCurveConstants.COORD_JACOBIAN:
+      begin
+        result := TFpPoint.Create(ecCurve, x1, Y1,
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z1), IsCompressed);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+      begin
+        result := TFpPoint.Create(ecCurve, x1, Y1,
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z1, W1), IsCompressed);
+        Exit;
+      end
+  else
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes
+        (@SUnSupportedCoordinateSystem);
+    end;
+
+  end;
+
+end;
+
+function TFpPoint.Twice: IECPoint;
+var
+  ecCurve: IECCurve;
+  Y1, x1, X1Squared, gamma, x3, Y3, Z1, w, s, t, b, _4B, h, _2s, _2t,
+    _4sSquared, Z3, m, Y1Squared, a4, a4Neg, Z1Squared, Z1Pow4: IECFieldElement;
+  coord: Int32;
+  Z1IsOne: Boolean;
+begin
+  if (IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  ecCurve := curve;
+
+  Y1 := RawYCoord;
+
+  if (Y1.IsZero) then
+  begin
+    result := ecCurve.Infinity;
+    Exit;
+  end;
+
+  coord := ecCurve.CoordinateSystem;
+
+  x1 := RawXCoord;
+
+  case coord of
+    TECCurveConstants.COORD_AFFINE:
+      begin
+        X1Squared := x1.Square();
+        gamma := Three(X1Squared).Add(curve.a).Divide(Two(Y1));
+        x3 := gamma.Square().Subtract(Two(x1));
+        Y3 := gamma.Multiply(x1.Subtract(x3)).Subtract(Y1);
+
+        result := TFpPoint.Create(curve, x3, Y3, IsCompressed);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_HOMOGENEOUS:
+      begin
+        Z1 := RawZCoords[0];
+
+        Z1IsOne := Z1.IsOne;
+
+        // TODO Optimize for small negative a4 and -3
+        w := ecCurve.a;
+        if ((not w.IsZero) and (not Z1IsOne)) then
+        begin
+          w := w.Multiply(Z1.Square());
+        end;
+        w := w.Add(Three(x1.Square()));
+
+        if Z1IsOne then
+        begin
+          s := Y1;
+        end
+        else
+        begin
+          s := Y1.Multiply(Z1);
+        end;
+
+        if Z1IsOne then
+        begin
+          t := Y1.Square();
+        end
+        else
+        begin
+          t := s.Multiply(Y1);
+        end;
+
+        b := x1.Multiply(t);
+        _4B := Four(b);
+        h := w.Square().Subtract(Two(_4B));
+
+        _2s := Two(s);
+        x3 := h.Multiply(_2s);
+        _2t := Two(t);
+        Y3 := _4B.Subtract(h).Multiply(w).Subtract(Two(_2t.Square()));
+
+        if Z1IsOne then
+        begin
+          _4sSquared := Two(_2t);
+        end
+        else
+        begin
+          _4sSquared := _2s.Square();
+        end;
+
+        Z3 := Two(_4sSquared).Multiply(s);
+
+        result := TFpPoint.Create(ecCurve, x3, Y3,
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z3), IsCompressed);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_JACOBIAN:
+      begin
+        Z1 := RawZCoords[0];
+
+        Z1IsOne := Z1.IsOne;
+
+        Y1Squared := Y1.Square();
+        t := Y1Squared.Square();
+
+        a4 := ecCurve.a;
+        a4Neg := a4.Negate();
+
+        if (a4Neg.ToBigInteger().Equals(TBigInteger.ValueOf(3))) then
+        begin
+
+          if Z1IsOne then
+          begin
+            Z1Squared := Z1;
+          end
+          else
+          begin
+            Z1Squared := Z1.Square();
+          end;
+
+          m := Three(x1.Add(Z1Squared).Multiply(x1.Subtract(Z1Squared)));
+          s := Four(Y1Squared.Multiply(x1));
+        end
+        else
+        begin
+          X1Squared := x1.Square();
+          m := Three(X1Squared);
+          if (Z1IsOne) then
+          begin
+            m := m.Add(a4);
+          end
+          else if (not a4.IsZero) then
+          begin
+
+            if Z1IsOne then
+            begin
+              Z1Squared := Z1;
+            end
+            else
+            begin
+              Z1Squared := Z1.Square();
+            end;
+
+            Z1Pow4 := Z1Squared.Square();
+            if (a4Neg.BitLength < a4.BitLength) then
+            begin
+              m := m.Subtract(Z1Pow4.Multiply(a4Neg));
+            end
+            else
+            begin
+              m := m.Add(Z1Pow4.Multiply(a4));
+            end
+          end;
+          // S := two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T));
+          s := Four(x1.Multiply(Y1Squared));
+        end;
+
+        x3 := m.Square().Subtract(Two(s));
+        Y3 := s.Subtract(x3).Multiply(m).Subtract(Eight(t));
+
+        Z3 := Two(Y1);
+        if (not Z1IsOne) then
+        begin
+          Z3 := Z3.Multiply(Z1);
+        end;
+
+        // Alternative calculation of Z3 using fast square
+        // Z3 := doubleProductFromSquares(Y1, Z1, Y1Squared, Z1Squared);
+        result := TFpPoint.Create(ecCurve, x3, Y3,
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z3), IsCompressed);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+      begin
+        result := TwiceJacobianModified(true);
+        Exit;
+      end
+  else
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes
+        (@SUnSupportedCoordinateSystem);
+    end;
+
+  end;
+end;
+
+function TFpPoint.TwiceJacobianModified(calculateW: Boolean): IFpPoint;
+var
+  x1, Y1, Z1, W1, X1Squared, m, _2Y1, _2Y1Squared, s, x3, _4T, _8T, Y3, W3,
+    Z3: IECFieldElement;
+begin
+  x1 := RawXCoord;
+  Y1 := RawYCoord;
+  Z1 := RawZCoords[0];
+  W1 := GetJacobianModifiedW();
+
+  X1Squared := x1.Square();
+  m := Three(X1Squared).Add(W1);
+  _2Y1 := Two(Y1);
+  _2Y1Squared := _2Y1.Multiply(Y1);
+  s := Two(x1.Multiply(_2Y1Squared));
+  x3 := m.Square().Subtract(Two(s));
+  _4T := _2Y1Squared.Square();
+  _8T := Two(_4T);
+  Y3 := m.Multiply(s.Subtract(x3)).Subtract(_8T);
+
+  if calculateW then
+  begin
+    W3 := Two(_8T.Multiply(W1));
+  end
+  else
+  begin
+    W3 := Nil;
+  end;
+
+  if Z1.IsOne then
+  begin
+    Z3 := _2Y1;
+  end
+  else
+  begin
+    Z3 := _2Y1.Multiply(Z1);
+  end;
+
+  result := TFpPoint.Create(curve, x3, Y3,
+    TCryptoLibGenericArray<IECFieldElement>.Create(Z3, W3), IsCompressed);
+end;
+
+function TFpPoint.TwicePlus(const b: IECPoint): IECPoint;
+var
+  Y1, x1, x2, Y2, dx, dy, Lx, LY, d, i, L1, L2, X4, Y4, bigD: IECFieldElement;
+  ecCurve: IECCurve;
+  coord: Int32;
+
+begin
+  if (Self as IECPoint = b) then
+  begin
+    result := ThreeTimes();
+    Exit;
+  end;
+  if (IsInfinity) then
+  begin
+    result := b;
+    Exit;
+  end;
+  if (b.IsInfinity) then
+  begin
+    result := Twice();
+    Exit;
+  end;
+
+  Y1 := RawYCoord;
+  if (Y1.IsZero) then
+  begin
+    result := b;
+    Exit;
+  end;
+
+  ecCurve := curve;
+  coord := ecCurve.CoordinateSystem;
+
+  case coord of
+    TECCurveConstants.COORD_AFFINE:
+      begin
+        x1 := RawXCoord;
+        x2 := b.RawXCoord;
+        Y2 := b.RawYCoord;
+
+        dx := x2.Subtract(x1);
+        dy := Y2.Subtract(Y1);
+
+        if (dx.IsZero) then
+        begin
+          if (dy.IsZero) then
+          begin
+            // this == b i.e. the result is 3P
+            result := ThreeTimes();
+            Exit;
+          end;
+
+          // this == -b, i.e. the result is P
+          result := Self;
+          Exit;
+        end;
+
+        // / * * Optimized calculation of 2 p + Q, as described
+        // in " Trading Inversions for * Multiplications
+        // in Elliptic curve Cryptography ", by Ciet, Joye, Lauter,
+        // Montgomery. * /
+
+        Lx := dx.Square();
+        LY := dy.Square();
+        d := Lx.Multiply(Two(x1).Add(x2)).Subtract(LY);
+        if (d.IsZero) then
+        begin
+          result := curve.Infinity;
+          Exit;
+        end;
+
+        bigD := d.Multiply(dx);
+        i := bigD.Invert();
+        L1 := d.Multiply(i).Multiply(dy);
+        L2 := Two(Y1).Multiply(Lx).Multiply(dx).Multiply(i).Subtract(L1);
+        X4 := (L2.Subtract(L1)).Multiply(L1.Add(L2)).Add(x2);
+        Y4 := (x1.Subtract(X4)).Multiply(L2).Subtract(Y1);
+
+        result := TFpPoint.Create(curve, X4, Y4, IsCompressed);
+        Exit;
+      end;
+    TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+      begin
+        result := TwiceJacobianModified(false).Add(b);
+        Exit;
+      end
+  else
+    begin
+      result := Twice().Add(b);
+      Exit;
+    end;
+  end;
+
+end;
+
+function TFpPoint.Two(const x: IECFieldElement): IECFieldElement;
+begin
+  result := x.Add(x);
+end;
+
+{ TAbstractF2mPoint }
+
+constructor TAbstractF2mPoint.Create(const curve: IECCurve;
+  const x, y: IECFieldElement; withCompression: Boolean);
+begin
+  Inherited Create(curve, x, y, withCompression);
+end;
+
+constructor TAbstractF2mPoint.Create(const curve: IECCurve;
+  const x, y: IECFieldElement;
+  const zs: TCryptoLibGenericArray<IECFieldElement>; withCompression: Boolean);
+begin
+  Inherited Create(curve, x, y, zs, withCompression);
+end;
+
+destructor TAbstractF2mPoint.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TAbstractF2mPoint.SatisfiesCurveEquation: Boolean;
+var
+  z, Z2, Z3, x, LY, a, b, lhs, rhs, L, x2, Z4: IECFieldElement;
+  ecCurve: IECCurve;
+  coord: Int32;
+  ZIsOne: Boolean;
+begin
+  ecCurve := curve;
+  x := RawXCoord;
+  LY := RawYCoord;
+  a := ecCurve.a;
+  b := ecCurve.b;
+
+  coord := ecCurve.CoordinateSystem;
+  if (coord = TECCurveConstants.COORD_LAMBDA_PROJECTIVE) then
+  begin
+    z := RawZCoords[0];
+    ZIsOne := z.IsOne;
+
+    if (x.IsZero) then
+    begin
+      // NOTE: For x == 0, we expect the affine-y instead of the lambda-y
+      lhs := LY.Square();
+      rhs := b;
+      if (not ZIsOne) then
+      begin
+        Z2 := z.Square();
+        rhs := rhs.Multiply(Z2);
+      end
+    end
+    else
+    begin
+      L := LY;
+      x2 := x.Square();
+      if (ZIsOne) then
+      begin
+        lhs := L.Square().Add(L).Add(a);
+        rhs := x2.Square().Add(b);
+      end
+      else
+      begin
+        Z2 := z.Square();
+        Z4 := Z2.Square();
+        lhs := L.Add(z).MultiplyPlusProduct(L, a, Z2);
+        // TODO If sqrt(b) is precomputed this can be simplified to a single square
+        rhs := x2.SquarePlusProduct(b, Z4);
+      end;
+      lhs := lhs.Multiply(x2);
+    end
+  end
+  else
+  begin
+    lhs := LY.Add(x).Multiply(LY);
+
+    case coord of
+      TECCurveConstants.COORD_AFFINE:
+        begin
+          // do nothing;
+        end;
+
+      TECCurveConstants.COORD_HOMOGENEOUS:
+        begin
+          z := RawZCoords[0];
+          if (not z.IsOne) then
+          begin
+            Z2 := z.Square();
+            Z3 := z.Multiply(Z2);
+            lhs := lhs.Multiply(z);
+            a := a.Multiply(z);
+            b := b.Multiply(Z3);
+          end;
+        end
+
+    else
+      begin
+        raise EInvalidOperationCryptoLibException.CreateRes
+          (@SUnSupportedCoordinateSystem);
+      end;
+
+    end;
+
+    rhs := x.Add(a).Multiply(x.Square()).Add(b);
+  end;
+
+  result := lhs.Equals(rhs);
+end;
+
+function TAbstractF2mPoint.SatisfiesOrder: Boolean;
+var
+  Cofactor: TBigInteger;
+  n: IECPoint;
+  x, rhs, lambda, w, t: IECFieldElement;
+  Lcurve: IECCurve;
+begin
+  Lcurve := curve;
+  Cofactor := Lcurve.GetCofactor();
+  if (TBigInteger.Two.Equals(Cofactor)) then
+  begin
+    // /*
+    // *  Check that the trace of (X + A) is 0, then there exists a solution to L^2 + L = X + A,
+    // *  and so a halving is possible, so this point is the double of another.
+    // */
+    n := Normalize();
+    x := n.AffineXCoord;
+    rhs := x.Add(Lcurve.a);
+    result := (rhs as IAbstractF2mFieldElement).Trace() = 0;
+    Exit;
+  end;
+  if (TBigInteger.Four.Equals(Cofactor)) then
+  begin
+    // /*
+    // * Solve L^2 + L = X + A to find the half of this point, if it exists (fail if not).
+    // * Generate both possibilities for the square of the half-point's x-coordinate (w),
+    // * and check if Tr(w + A) == 0 for at least one; then a second halving is possible
+    // * (see comments for cofactor 2 above), so this point is four times another.
+    // *
+    // * Note: Tr(x^2) == Tr(x).
+    // */
+    n := Normalize();
+    x := n.AffineXCoord;
+    lambda := (Lcurve as IAbstractF2mCurve).SolveQuadraticEquation
+      (x.Add(curve.a));
+    if (lambda = Nil) then
+    begin
+      result := false;
+      Exit;
+    end;
+    w := x.Multiply(lambda).Add(n.AffineYCoord);
+    t := w.Add(Lcurve.a);
+    result := ((t as IAbstractF2mFieldElement).Trace() = 0) or
+      ((t.Add(x) as IAbstractF2mFieldElement).Trace() = 0);
+    Exit;
+  end;
+
+  result := Inherited SatisfiesOrder();
+end;
+
+function TAbstractF2mPoint.ScaleX(const scale: IECFieldElement): IECPoint;
+var
+  Lx, L, x2, L2, z, Z2: IECFieldElement;
+begin
+  if (IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  case CurveCoordinateSystem of
+    TECCurveConstants.COORD_LAMBDA_AFFINE:
+      begin
+        // Y is actually Lambda (X + Y/X) here
+        Lx := RawXCoord;
+        L := RawYCoord;
+
+        x2 := Lx.Multiply(scale);
+        L2 := L.Add(Lx).Divide(scale).Add(x2);
+
+        result := curve.CreateRawPoint(Lx, L2, RawZCoords, IsCompressed);
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        // Y is actually Lambda (X + Y/X) here
+        Lx := RawXCoord;
+        L := RawYCoord;
+        z := RawZCoords[0];
+
+        // We scale the Z coordinate also, to avoid an inversion
+        x2 := Lx.Multiply(scale.Square());
+        L2 := L.Add(Lx).Add(x2);
+        Z2 := z.Multiply(scale);
+
+        result := curve.CreateRawPoint(Lx, L2,
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z2), IsCompressed);
+        Exit;
+      end
+  else
+    begin
+      result := (Inherited ScaleX(scale));
+    end;
+
+  end;
+
+end;
+
+function TAbstractF2mPoint.ScaleY(const scale: IECFieldElement): IECPoint;
+var
+  Lx, L, L2: IECFieldElement;
+begin
+  if (IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  case CurveCoordinateSystem of
+    TECCurveConstants.COORD_LAMBDA_AFFINE,
+      TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        Lx := RawXCoord;
+        L := RawYCoord;
+
+        // Y is actually Lambda (X + Y/X) here
+        L2 := L.Add(Lx).Multiply(scale).Add(Lx);
+
+        result := curve.CreateRawPoint(Lx, L2, RawZCoords, IsCompressed);
+        Exit;
+      end
+  else
+    begin
+      result := (Inherited ScaleY(scale));
+    end;
+  end;
+
+end;
+
+function TAbstractF2mPoint.Subtract(const b: IECPoint): IECPoint;
+begin
+  if (b.IsInfinity) then
+  begin
+    result := Self;
+  end;
+
+  // Add -b
+  result := Add(b.Negate());
+end;
+
+function TAbstractF2mPoint.Tau: IAbstractF2mPoint;
+var
+  ecCurve: IECCurve;
+  coord: Int32;
+  x1, Y1, Z1: IECFieldElement;
+begin
+  if (IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  ecCurve := curve;
+  coord := ecCurve.CoordinateSystem;
+
+  x1 := RawXCoord;
+
+  case coord of
+    TECCurveConstants.COORD_AFFINE, TECCurveConstants.COORD_LAMBDA_AFFINE:
+      begin
+        Y1 := RawYCoord;
+        result := ecCurve.CreateRawPoint(x1.Square(), Y1.Square(), IsCompressed)
+          as IAbstractF2mPoint;
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_HOMOGENEOUS,
+      TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        Y1 := RawYCoord;
+        Z1 := RawZCoords[0];
+        result := ecCurve.CreateRawPoint(x1.Square(), Y1.Square(),
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z1.Square()),
+          IsCompressed) as IAbstractF2mPoint;
+        Exit;
+      end
+
+  else
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes
+        (@SUnSupportedCoordinateSystem);
+    end;
+
+  end;
+end;
+
+function TAbstractF2mPoint.TauPow(pow: Int32): IAbstractF2mPoint;
+var
+  ecCurve: IECCurve;
+  coord: Int32;
+  x1, Y1, Z1: IECFieldElement;
+begin
+  if (IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  ecCurve := curve;
+  coord := ecCurve.CoordinateSystem;
+
+  x1 := RawXCoord;
+
+  case coord of
+    TECCurveConstants.COORD_AFFINE, TECCurveConstants.COORD_LAMBDA_AFFINE:
+      begin
+        Y1 := RawYCoord;
+        result := ecCurve.CreateRawPoint(x1.SquarePow(pow), Y1.SquarePow(pow),
+          IsCompressed) as IAbstractF2mPoint;
+        Exit;
+      end;
+
+    TECCurveConstants.COORD_HOMOGENEOUS,
+      TECCurveConstants.COORD_LAMBDA_PROJECTIVE:
+      begin
+        Y1 := RawYCoord;
+        Z1 := RawZCoords[0];
+        result := ecCurve.CreateRawPoint(x1.SquarePow(pow), Y1.SquarePow(pow),
+          TCryptoLibGenericArray<IECFieldElement>.Create(Z1.SquarePow(pow)),
+          IsCompressed) as IAbstractF2mPoint;
+        Exit;
+      end
+
+  else
+    begin
+      raise EInvalidOperationCryptoLibException.CreateRes
+        (@SUnSupportedCoordinateSystem);
+    end;
+
+  end;
+end;
+
+{ TECPoint.TValidityCallback }
+
+constructor TECPoint.TValidityCallback.Create(const outer: IECPoint;
+  decompressed, checkOrder: Boolean);
+begin
+  Inherited Create();
+  Fm_outer := outer;
+  Fm_decompressed := decompressed;
+  Fm_checkOrder := checkOrder;
+end;
+
+function TECPoint.TValidityCallback.Precompute(const existing: IPreCompInfo)
+  : IPreCompInfo;
+var
+  info: IValidityPrecompInfo;
+begin
+  if (not(Supports(existing, IValidityPrecompInfo, info))) then
+  begin
+    info := TValidityPrecompInfo.Create();
+  end;
+
+  if (info.hasFailed()) then
+  begin
+    result := info;
+    Exit;
+  end;
+  if (not(info.hasCurveEquationPassed())) then
+  begin
+    if (not(Fm_decompressed) and not(Fm_outer.SatisfiesCurveEquation())) then
+    begin
+      info.reportFailed();
+      result := info;
+      Exit;
+    end;
+    info.reportCurveEquationPassed();
+  end;
+
+  if ((Fm_checkOrder) and (not(info.HasOrderPassed()))) then
+  begin
+    if (not(Fm_outer.SatisfiesOrder())) then
+    begin
+      info.reportFailed();
+      result := info;
+      Exit;
+    end;
+    info.reportOrderPassed();
+  end;
+  result := info;
+end;
+
+end.

+ 43 - 0
src/libraries/cryptolib4pascal/ClpECCurveConstants.pas

@@ -0,0 +1,43 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECCurveConstants;
+
+{$I CryptoLib.inc}
+
+interface
+
+type
+  TECCurveConstants = class sealed(TObject)
+
+  public
+
+    const
+    COORD_AFFINE = Int32(0);
+    COORD_HOMOGENEOUS = Int32(1);
+    COORD_JACOBIAN = Int32(2);
+    COORD_JACOBIAN_CHUDNOVSKY = Int32(3);
+    COORD_JACOBIAN_MODIFIED = Int32(4);
+    COORD_LAMBDA_AFFINE = Int32(5);
+    COORD_LAMBDA_PROJECTIVE = Int32(6);
+    COORD_SKEWED = Int32(7);
+
+  end;
+
+implementation
+
+end.

+ 152 - 0
src/libraries/cryptolib4pascal/ClpECDHBasicAgreement.pas

@@ -0,0 +1,152 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECDHBasicAgreement;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpBigInteger,
+  ClpECAlgorithms,
+  ClpICipherParameters,
+  ClpIECDomainParameters,
+  ClpIECC,
+  ClpIBasicAgreement,
+  ClpIECDHBasicAgreement,
+  ClpIECPrivateKeyParameters,
+  ClpIParametersWithRandom,
+  ClpIECPublicKeyParameters,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SWrongDomainParameter = 'ECDH Public Key has Wrong Domain Parameters';
+  SInvalidAgreementValue = 'Infinity is not a Valid Agreement Value for ECDH';
+  SInfinityInvalidPublicKey = 'Infinity is not a Valid Public Key for ECDH';
+
+type
+  /// <summary>
+  /// P1363 7.2.1 ECSVDP-DH <br />ECSVDP-DH is Elliptic Curve Secret Value
+  /// Derivation Primitive, <br />Diffie-Hellman version. It is based on the
+  /// work of [DH76], [Mil86], <br />and [Kob87]. This primitive derives a
+  /// shared secret value from one <br />party's private key and another
+  /// party's public key, where both have <br />the same set of EC domain
+  /// parameters. If two parties correctly <br />execute this primitive, they
+  /// will produce the same output. This <br />primitive can be invoked by a
+  /// scheme to derive a shared secret key; <br />specifically, it may be
+  /// used with the schemes ECKAS-DH1 and <br />DL/ECKAS-DH2. It assumes that
+  /// the input keys are valid (see also <br />Section 7.2.2). <br />
+  /// </summary>
+  TECDHBasicAgreement = class(TInterfacedObject, IECDHBasicAgreement,
+    IBasicAgreement)
+
+  strict protected
+  var
+    FprivKey: IECPrivateKeyParameters;
+
+  public
+    /// <summary>
+    /// initialise the agreement engine.
+    /// </summary>
+    procedure Init(const parameters: ICipherParameters); virtual;
+
+    /// <summary>
+    /// return the field size for the agreement algorithm in bytes.
+    /// </summary>
+    function GetFieldSize(): Int32; virtual;
+
+    /// <summary>
+    /// given a public key from a given party calculate the next message
+    /// in the agreement sequence.
+    /// </summary>
+    function CalculateAgreement(const pubKey: ICipherParameters)
+      : TBigInteger; virtual;
+
+  end;
+
+implementation
+
+{ TECDHBasicAgreement }
+
+function TECDHBasicAgreement.CalculateAgreement(const pubKey: ICipherParameters)
+  : TBigInteger;
+var
+  pub: IECPublicKeyParameters;
+  params: IECDomainParameters;
+  P, Q: IECPoint;
+  d, h: TBigInteger;
+begin
+  pub := pubKey as IECPublicKeyParameters;
+  params := FprivKey.parameters;
+  if (not(params.Equals(pub.parameters))) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes(@SWrongDomainParameter);
+
+  end;
+
+  d := FprivKey.d;
+  // Always perform calculations on the exact curve specified by our private key's parameters
+
+  Q := TECAlgorithms.CleanPoint(params.Curve, pub.Q);
+
+  if (Q.IsInfinity) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SInfinityInvalidPublicKey);
+  end;
+
+  h := params.h;
+
+  if (not(h.Equals(TBigInteger.One))) then
+  begin
+    d := params.HInv.Multiply(d).&Mod(params.N);
+    Q := TECAlgorithms.ReferenceMultiply(Q, h);
+  end;
+
+  P := Q.Multiply(d).Normalize();
+
+  if (P.IsInfinity) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SInvalidAgreementValue);
+
+  end;
+
+  result := P.AffineXCoord.ToBigInteger();
+end;
+
+function TECDHBasicAgreement.GetFieldSize: Int32;
+begin
+  result := (FprivKey.parameters.Curve.FieldSize + 7) div 8;
+end;
+
+procedure TECDHBasicAgreement.Init(const parameters: ICipherParameters);
+var
+  Lparameters: ICipherParameters;
+begin
+  Lparameters := parameters;
+  if Supports(Lparameters, IParametersWithRandom) then
+  begin
+    Lparameters := (Lparameters as IParametersWithRandom).parameters;
+  end;
+
+  FprivKey := Lparameters as IECPrivateKeyParameters;
+end;
+
+end.

+ 148 - 0
src/libraries/cryptolib4pascal/ClpECDHCBasicAgreement.pas

@@ -0,0 +1,148 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECDHCBasicAgreement;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpBigInteger,
+  ClpECAlgorithms,
+  ClpICipherParameters,
+  ClpIECC,
+  ClpIBasicAgreement,
+  ClpIECDomainParameters,
+  ClpIECDHCBasicAgreement,
+  ClpIECPrivateKeyParameters,
+  ClpIParametersWithRandom,
+  ClpIECPublicKeyParameters,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SWrongDomainParameter = 'ECDHC Public Key has Wrong Domain Parameters';
+  SInvalidAgreementValue = 'Infinity is not a Valid Agreement Value for ECDHC';
+  SInfinityInvalidPublicKey = 'Infinity is not a Valid Public Key for ECDHC';
+
+type
+  /// <summary>
+  /// P1363 7.2.2 ECSVDP-DHC <br /><br />ECSVDP-DHC is Elliptic Curve Secret
+  /// Value Derivation Primitive, <br />Diffie-Hellman version with cofactor
+  /// multiplication. It is based on <br />the work of [DH76], [Mil86],
+  /// [Kob87], [LMQ98] and [Kal98a]. This <br />primitive derives a shared
+  /// secret value from one party's private key <br />and another party's
+  /// public key, where both have the same set of EC <br />domain parameters.
+  /// If two parties correctly execute this primitive, <br />they will
+  /// produce the same output. This primitive can be invoked by a <br />
+  /// scheme to derive a shared secret key; specifically, it may be used <br />
+  /// with the schemes ECKAS-DH1 and DL/ECKAS-DH2. It does not assume the <br />
+  /// validity of the input public key (see also Section 7.2.1). <br /><br />
+  /// Note: As stated P1363 compatibility mode with ECDH can be preset, and <br />
+  /// in this case the implementation doesn't have a ECDH compatibility mode <br />
+  /// (if you want that just use ECDHBasicAgreement and note they both
+  /// implement <br />BasicAgreement!). <br />
+  /// </summary>
+  TECDHCBasicAgreement = class(TInterfacedObject, IECDHCBasicAgreement,
+    IBasicAgreement)
+
+  strict protected
+  var
+    FprivKey: IECPrivateKeyParameters;
+
+  public
+    /// <summary>
+    /// initialise the agreement engine.
+    /// </summary>
+    procedure Init(const parameters: ICipherParameters); virtual;
+
+    /// <summary>
+    /// return the field size for the agreement algorithm in bytes.
+    /// </summary>
+    function GetFieldSize(): Int32; virtual;
+
+    /// <summary>
+    /// given a public key from a given party calculate the next message
+    /// in the agreement sequence.
+    /// </summary>
+    function CalculateAgreement(const pubKey: ICipherParameters)
+      : TBigInteger; virtual;
+
+  end;
+
+implementation
+
+{ TECDHCBasicAgreement }
+
+function TECDHCBasicAgreement.CalculateAgreement(const pubKey
+  : ICipherParameters): TBigInteger;
+var
+  pub: IECPublicKeyParameters;
+  params: IECDomainParameters;
+  hd: TBigInteger;
+  P, pubPoint: IECPoint;
+begin
+  pub := pubKey as IECPublicKeyParameters;
+  params := FprivKey.parameters;
+  if (not(params.Equals(pub.parameters))) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes(@SWrongDomainParameter);
+
+  end;
+
+  hd := params.H.Multiply(FprivKey.D).&Mod(params.N);
+
+  // Always perform calculations on the exact curve specified by our private key's parameters
+  pubPoint := TECAlgorithms.CleanPoint(params.Curve, pub.Q);
+  if (pubPoint.IsInfinity) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SInfinityInvalidPublicKey);
+  end;
+
+  P := pubPoint.Multiply(hd).Normalize();
+
+  if (P.IsInfinity) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SInvalidAgreementValue);
+
+  end;
+
+  result := P.AffineXCoord.ToBigInteger();
+end;
+
+function TECDHCBasicAgreement.GetFieldSize: Int32;
+begin
+  result := (FprivKey.parameters.Curve.FieldSize + 7) div 8;
+end;
+
+procedure TECDHCBasicAgreement.Init(const parameters: ICipherParameters);
+var
+  Lparameters: ICipherParameters;
+begin
+  Lparameters := parameters;
+  if Supports(Lparameters, IParametersWithRandom) then
+  begin
+    Lparameters := (Lparameters as IParametersWithRandom).parameters;
+  end;
+
+  FprivKey := Lparameters as IECPrivateKeyParameters;
+end;
+
+end.

+ 237 - 0
src/libraries/cryptolib4pascal/ClpECDomainParameters.pas

@@ -0,0 +1,237 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECDomainParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SyncObjs,
+  ClpBigInteger,
+  ClpECAlgorithms,
+  ClpIECC,
+  ClpCryptoLibTypes,
+  ClpIECDomainParameters;
+
+resourcestring
+  SCurveNil = 'Curve Cannot be Nil';
+  SGNil = 'G Cannot be Nil';
+  SBigIntegerNotInitialized = 'BigInteger Not Initialized "%s"';
+  SQNil = 'Q Cannot be Nil';
+  SQInfinity = 'Point at Infinity "Q"';
+  SQPointNotOnCurve = 'Point Not on Curve "Q"';
+
+type
+
+  TECDomainParameters = class sealed(TInterfacedObject, IECDomainParameters)
+
+  strict private
+
+    class var
+
+      FLock: TCriticalSection;
+
+  var
+    Fcurve: IECCurve;
+    Fseed: TCryptoLibByteArray;
+    Fg: IECPoint;
+    Fn, Fh, FhInv: TBigInteger;
+
+    function GetCurve: IECCurve; inline;
+    function GetG: IECPoint; inline;
+    function GetH: TBigInteger; inline;
+    function GetN: TBigInteger; inline;
+    function GetHInv: TBigInteger; inline;
+    function GetSeed: TCryptoLibByteArray; inline;
+
+    class procedure Boot(); static;
+    class constructor CreateECDomainParameters();
+    class destructor DestroyECDomainParameters();
+
+  public
+
+    class function Validate(const c: IECCurve; const q: IECPoint)
+      : IECPoint; static;
+
+    constructor Create(const curve: IECCurve; const g: IECPoint;
+      const n: TBigInteger); overload;
+    constructor Create(const curve: IECCurve; const g: IECPoint;
+      const n, h: TBigInteger); overload;
+    constructor Create(const curve: IECCurve; const g: IECPoint;
+      const n, h: TBigInteger; const seed: TCryptoLibByteArray); overload;
+
+    property curve: IECCurve read GetCurve;
+    property g: IECPoint read GetG;
+    property n: TBigInteger read GetN;
+    property h: TBigInteger read GetH;
+    property HInv: TBigInteger read GetHInv;
+    property seed: TCryptoLibByteArray read GetSeed;
+    function Equals(const other: IECDomainParameters): Boolean; reintroduce;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+  end;
+
+implementation
+
+{ TECDomainParameters }
+
+class function TECDomainParameters.Validate(const c: IECCurve;
+  const q: IECPoint): IECPoint;
+begin
+  if (q = Nil) then
+    raise EArgumentNilCryptoLibException.CreateRes(@SQNil);
+
+  result := TECAlgorithms.ImportPoint(c, q).Normalize();
+
+  if (result.IsInfinity) then
+    raise EArgumentCryptoLibException.CreateRes(@SQInfinity);
+
+  if (not(result.IsValid())) then
+    raise EArgumentCryptoLibException.CreateRes(@SQPointNotOnCurve);
+
+end;
+
+function TECDomainParameters.GetCurve: IECCurve;
+begin
+  result := Fcurve;
+end;
+
+function TECDomainParameters.GetG: IECPoint;
+begin
+  result := Fg;
+end;
+
+function TECDomainParameters.GetH: TBigInteger;
+begin
+  result := Fh;
+end;
+
+function TECDomainParameters.GetN: TBigInteger;
+begin
+  result := Fn;
+end;
+
+function TECDomainParameters.GetHInv: TBigInteger;
+begin
+
+  FLock.Acquire;
+  try
+    if (not(FhInv.IsInitialized)) then
+    begin
+      FhInv := h.ModInverse(n);
+    end;
+    result := FhInv;
+  finally
+    FLock.Release;
+  end;
+end;
+
+function TECDomainParameters.GetSeed: TCryptoLibByteArray;
+begin
+  result := System.Copy(Fseed);
+end;
+
+constructor TECDomainParameters.Create(const curve: IECCurve; const g: IECPoint;
+  const n: TBigInteger);
+begin
+  Create(curve, g, n, TBigInteger.One, Nil);
+end;
+
+constructor TECDomainParameters.Create(const curve: IECCurve; const g: IECPoint;
+  const n, h: TBigInteger);
+begin
+  Create(curve, g, n, h, Nil);
+end;
+
+class procedure TECDomainParameters.Boot;
+begin
+  if FLock = Nil then
+  begin
+    FLock := TCriticalSection.Create;
+  end;
+end;
+
+constructor TECDomainParameters.Create(const curve: IECCurve; const g: IECPoint;
+  const n, h: TBigInteger; const seed: TCryptoLibByteArray);
+begin
+  if (curve = Nil) then
+    raise EArgumentNilCryptoLibException.CreateRes(@SCurveNil);
+  if (g = Nil) then
+    raise EArgumentNilCryptoLibException.CreateRes(@SGNil);
+
+  if (not n.IsInitialized) then
+    raise EArgumentNilCryptoLibException.CreateResFmt
+      (@SBigIntegerNotInitialized, ['n']);
+
+  // we can't check for (not (h.IsInitialized)) here as h is optional in X9.62 as it is not required for ECDSA
+
+  Fcurve := curve;
+  Fg := Validate(curve, g);
+  Fn := n;
+  Fh := h;
+  FhInv := Default (TBigInteger);
+
+  Fseed := System.Copy(seed);
+
+end;
+
+class constructor TECDomainParameters.CreateECDomainParameters;
+begin
+  TECDomainParameters.Boot;
+end;
+
+class destructor TECDomainParameters.DestroyECDomainParameters;
+begin
+  FLock.Free;
+end;
+
+function TECDomainParameters.Equals(const other: IECDomainParameters): Boolean;
+begin
+
+  if (other = Self as IECDomainParameters) then
+  begin
+    result := True;
+    Exit;
+  end;
+
+  if (other = Nil) then
+  begin
+    result := false;
+    Exit;
+  end;
+
+  result := curve.Equals(other.curve) and g.Equals(other.g) and
+    n.Equals(other.n) and h.Equals(other.h);
+
+end;
+
+function TECDomainParameters.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := curve.GetHashCode();
+  result := result * 37;
+  result := result xor g.GetHashCode();
+  result := result * 37;
+  result := result xor n.GetHashCode();
+  result := result * 37;
+  result := result xor h.GetHashCode();
+end;
+
+end.

+ 402 - 0
src/libraries/cryptolib4pascal/ClpECDsaSigner.pas

@@ -0,0 +1,402 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECDsaSigner;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpBigInteger,
+  ClpSecureRandom,
+  ClpECAlgorithms,
+  ClpECC,
+  ClpIECC,
+  ClpIParametersWithRandom,
+  ClpIECPublicKeyParameters,
+  ClpIECPrivateKeyParameters,
+  ClpFixedPointCombMultiplier,
+  ClpCryptoLibTypes,
+  ClpICipherParameters,
+  ClpIECDomainParameters,
+  ClpISecureRandom,
+  ClpRandomDsaKCalculator,
+  ClpIECKeyParameters,
+  ClpIDsaKCalculator,
+  ClpECCurveConstants,
+  ClpIDsaExt,
+  ClpIECDsaSigner;
+
+resourcestring
+  SECPublicKeyNotFound = 'EC Public Key Required for Verification';
+  SECPrivateKeyNotFound = 'EC Private Key Required for Signing';
+
+type
+
+  /// <summary>
+  /// EC-DSA as described in X9.62
+  /// </summary>
+  TECDsaSigner = class(TInterfacedObject, IDsaExt, IECDsaSigner)
+
+  strict private
+
+    class var
+
+      FEight: TBigInteger;
+
+    class procedure Boot(); static;
+    class constructor ECDsaSigner();
+
+    class function GetEight: TBigInteger; static; inline;
+
+    class property Eight: TBigInteger read GetEight;
+
+    function GetOrder: TBigInteger; virtual;
+    function GetAlgorithmName: String; virtual;
+
+  strict protected
+  var
+    FkCalculator: IDsaKCalculator;
+    Fkey: IECKeyParameters;
+    Frandom: ISecureRandom;
+
+    function CalculateE(const n: TBigInteger;
+      const &message: TCryptoLibByteArray): TBigInteger; virtual;
+
+    function CreateBasePointMultiplier(): IECMultiplier; virtual;
+
+    function GetDenominator(coordinateSystem: Int32; const p: IECPoint)
+      : IECFieldElement; virtual;
+
+    function InitSecureRandom(needed: Boolean; const provided: ISecureRandom)
+      : ISecureRandom; virtual;
+
+  public
+
+    /// <summary>
+    /// Default configuration, random K values.
+    /// </summary>
+    constructor Create(); overload;
+
+    /// <summary>
+    /// Configuration with an alternate, possibly deterministic calculator of
+    /// K.
+    /// </summary>
+    /// <param name="kCalculator">
+    /// kCalculator a K value calculator.
+    /// </param>
+    constructor Create(const kCalculator: IDsaKCalculator); overload;
+
+    property Order: TBigInteger read GetOrder;
+    property AlgorithmName: String read GetAlgorithmName;
+
+    procedure Init(forSigning: Boolean;
+      const parameters: ICipherParameters); virtual;
+
+    // // 5.3 pg 28
+    // /**
+    // * Generate a signature for the given message using the key we were
+    // * initialised with. For conventional DSA the message should be a SHA-1
+    // * hash of the message of interest.
+    // *
+    // * @param message the message that will be verified later.
+    function GenerateSignature(const &message: TCryptoLibByteArray)
+      : TCryptoLibGenericArray<TBigInteger>; virtual;
+
+    // // 5.4 pg 29
+    // /**
+    // * return true if the value r and s represent a DSA signature for
+    // * the passed in message (for standard DSA the message should be
+    // * a SHA-1 hash of the real message to be verified).
+    // */
+    function VerifySignature(const &message: TCryptoLibByteArray;
+      const r, s: TBigInteger): Boolean;
+
+  end;
+
+implementation
+
+{ TECDsaSigner }
+
+constructor TECDsaSigner.Create;
+begin
+  inherited Create();
+  FkCalculator := TRandomDsaKCalculator.Create();
+end;
+
+class procedure TECDsaSigner.Boot;
+begin
+  FEight := TBigInteger.ValueOf(8);
+end;
+
+function TECDsaSigner.CalculateE(const n: TBigInteger;
+  const &message: TCryptoLibByteArray): TBigInteger;
+var
+  messageBitLength: Int32;
+  trunc: TBigInteger;
+begin
+  messageBitLength := System.Length(&message) * 8;
+  trunc := TBigInteger.Create(1, &message);
+
+  if (n.BitLength < messageBitLength) then
+  begin
+    trunc := trunc.ShiftRight(messageBitLength - n.BitLength);
+  end;
+
+  Result := trunc;
+end;
+
+constructor TECDsaSigner.Create(const kCalculator: IDsaKCalculator);
+begin
+  inherited Create();
+  FkCalculator := kCalculator;
+end;
+
+function TECDsaSigner.CreateBasePointMultiplier: IECMultiplier;
+begin
+  Result := TFixedPointCombMultiplier.Create();
+end;
+
+class constructor TECDsaSigner.ECDsaSigner;
+begin
+  TECDsaSigner.Boot;
+end;
+
+function TECDsaSigner.GenerateSignature(const &message: TCryptoLibByteArray)
+  : TCryptoLibGenericArray<TBigInteger>;
+var
+  ec: IECDomainParameters;
+  basePointMultiplier: IECMultiplier;
+  n, e, d, r, s, k: TBigInteger;
+  p: IECPoint;
+begin
+  ec := Fkey.parameters;
+  n := ec.n;
+  e := CalculateE(n, &message);
+  d := (Fkey as IECPrivateKeyParameters).d;
+
+  if (FkCalculator.IsDeterministic) then
+  begin
+    FkCalculator.Init(n, d, &message);
+  end
+  else
+  begin
+    FkCalculator.Init(n, Frandom);
+  end;
+
+  basePointMultiplier := CreateBasePointMultiplier();
+
+  // 5.3.2
+  repeat // Generate s
+
+    repeat // Generate r
+      k := FkCalculator.NextK();
+
+      p := basePointMultiplier.Multiply(ec.G, k).Normalize();
+
+      // 5.3.3
+      r := p.AffineXCoord.ToBigInteger().&Mod(n);
+    until (not(r.SignValue = 0));
+
+    s := k.ModInverse(n).Multiply(e.Add(d.Multiply(r))).&Mod(n);
+
+  until (not(s.SignValue = 0));
+
+  Result := TCryptoLibGenericArray<TBigInteger>.Create(r, s);
+end;
+
+function TECDsaSigner.GetAlgorithmName: String;
+begin
+  Result := 'ECDSA';
+end;
+
+function TECDsaSigner.GetDenominator(coordinateSystem: Int32; const p: IECPoint)
+  : IECFieldElement;
+begin
+  case (coordinateSystem) of
+    TECCurveConstants.COORD_HOMOGENEOUS,
+      TECCurveConstants.COORD_LAMBDA_PROJECTIVE, TECCurveConstants.COORD_SKEWED:
+      begin
+        Result := p.GetZCoord(0);
+      end;
+
+    TECCurveConstants.COORD_JACOBIAN,
+      TECCurveConstants.COORD_JACOBIAN_CHUDNOVSKY,
+      TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+      begin
+        Result := p.GetZCoord(0).Square();
+      end
+  else
+    begin
+      Result := Nil;
+    end;
+  end;
+end;
+
+class function TECDsaSigner.GetEight: TBigInteger;
+begin
+  Result := FEight;
+end;
+
+function TECDsaSigner.GetOrder: TBigInteger;
+begin
+  Result := Fkey.parameters.n;
+end;
+
+procedure TECDsaSigner.Init(forSigning: Boolean;
+  const parameters: ICipherParameters);
+var
+  providedRandom: ISecureRandom;
+  rParam: IParametersWithRandom;
+  Lparameters: ICipherParameters;
+begin
+  providedRandom := Nil;
+  Lparameters := parameters;
+
+  if (forSigning) then
+  begin
+
+    if (Supports(Lparameters, IParametersWithRandom, rParam)) then
+    begin
+      providedRandom := rParam.random;
+      Lparameters := rParam.parameters;
+    end;
+
+    if (not(Supports(Lparameters, IECPrivateKeyParameters))) then
+    begin
+      raise EInvalidKeyCryptoLibException.CreateRes(@SECPrivateKeyNotFound);
+    end;
+
+    Fkey := Lparameters as IECPrivateKeyParameters;
+  end
+  else
+  begin
+    if (not(Supports(Lparameters, IECPublicKeyParameters))) then
+    begin
+      raise EInvalidKeyCryptoLibException.CreateRes(@SECPublicKeyNotFound);
+    end;
+
+    Fkey := Lparameters as IECPublicKeyParameters;
+  end;
+
+  Frandom := InitSecureRandom((forSigning) and
+    (not FkCalculator.IsDeterministic), providedRandom);
+end;
+
+function TECDsaSigner.InitSecureRandom(needed: Boolean;
+  const provided: ISecureRandom): ISecureRandom;
+begin
+  if (not needed) then
+  begin
+    Result := Nil;
+  end
+  else
+  begin
+    if (provided <> Nil) then
+    begin
+      Result := provided;
+    end
+    else
+    begin
+      Result := TSecureRandom.Create();
+    end;
+  end;
+end;
+
+function TECDsaSigner.VerifySignature(const &message: TCryptoLibByteArray;
+  const r, s: TBigInteger): Boolean;
+var
+  n, e, c, u1, u2, cofactor, v, Smallr: TBigInteger;
+  G, Q, point: IECPoint;
+  curve: IECCurve;
+  d, X, RLocal: IECFieldElement;
+begin
+  n := Fkey.parameters.n;
+  Smallr := r;
+
+  // r and s should both in the range [1,n-1]
+  if ((Smallr.SignValue < 1) or (s.SignValue < 1) or (Smallr.CompareTo(n) >= 0)
+    or (s.CompareTo(n) >= 0)) then
+  begin
+    Result := false;
+    Exit;
+  end;
+
+  e := CalculateE(n, &message);
+  c := s.ModInverse(n);
+
+  u1 := e.Multiply(c).&Mod(n);
+  u2 := Smallr.Multiply(c).&Mod(n);
+
+  G := Fkey.parameters.G;
+
+  Q := (Fkey as IECPublicKeyParameters).Q;
+
+  point := TECAlgorithms.SumOfTwoMultiplies(G, u1, Q, u2);
+
+  if (point.IsInfinity) then
+  begin
+    Result := false;
+    Exit;
+  end;
+
+  // /*
+  // * If possible, avoid normalizing the point (to save a modular inversion in the curve field).
+  // *
+  // * There are ~cofactor elements of the curve field that reduce (modulo the group order) to 'r'.
+  // * If the cofactor is known and small, we generate those possible field values and project each
+  // * of them to the same "denominator" (depending on the particular projective coordinates in use)
+  // * as the calculated point.X. If any of the projected values matches point.X, then we have:
+  // *     (point.X / Denominator mod p) mod n == r
+  // * as required, and verification succeeds.
+  // *
+  // * Based on an original idea by Gregory Maxwell (https://github.com/gmaxwell), as implemented in
+  // * the libsecp256k1 project (https://github.com/bitcoin/secp256k1).
+  // */
+  curve := point.curve;
+  if (curve <> Nil) then
+  begin
+    cofactor := curve.cofactor;
+    if ((cofactor.IsInitialized) and (cofactor.CompareTo(Eight) <= 0)) then
+    begin
+      d := GetDenominator(curve.coordinateSystem, point);
+      if ((d <> Nil) and (not d.IsZero)) then
+      begin
+        X := point.XCoord;
+        while (curve.IsValidFieldElement(Smallr)) do
+        begin
+          RLocal := curve.FromBigInteger(Smallr).Multiply(d);
+          if (RLocal.Equals(X)) then
+          begin
+            Result := True;
+            Exit;
+          end;
+          Smallr := Smallr.Add(n);
+        end;
+        Result := false;
+        Exit;
+      end;
+    end;
+  end;
+
+  v := point.Normalize().AffineXCoord.ToBigInteger().&Mod(n);
+  Result := v.Equals(Smallr);
+end;
+
+end.

+ 409 - 0
src/libraries/cryptolib4pascal/ClpECGost3410NamedCurves.pas

@@ -0,0 +1,409 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECGost3410NamedCurves;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Generics.Collections,
+  ClpCryptoLibTypes,
+  ClpBigInteger,
+  ClpCryptoProObjectIdentifiers,
+  ClpRosstandartObjectIdentifiers,
+  ClpECC,
+  ClpIECC,
+  ClpECDomainParameters,
+  ClpIECDomainParameters,
+  ClpIAsn1Objects;
+
+type
+  /// <summary>
+  /// Table of the available named parameters for GOST 3410-2001 / 2012.
+  /// </summary>
+  TECGost3410NamedCurves = class sealed(TObject)
+
+  strict private
+  class var
+    Fparameters: TDictionary<IDerObjectIdentifier, IECDomainParameters>;
+    FobjIds: TDictionary<String, IDerObjectIdentifier>;
+    Fnames: TDictionary<IDerObjectIdentifier, String>;
+
+    class procedure Boot(); static;
+    class constructor CreateECGost3410NamedCurves();
+    class destructor DestroyECGost3410NamedCurves();
+
+    class function GetNames: TCryptoLibStringArray; static; inline;
+
+  public
+    // /**
+    // * return the ECDomainParameters object for the given OID, null if it
+    // * isn't present.
+    // *
+    // * @param oid an object identifier representing a named parameters, if present.
+    // */
+    class function GetByOid(const oid: IDerObjectIdentifier)
+      : IECDomainParameters; static; inline;
+    // /**
+    // * return the ECDomainParameters object for the given OID, null if it
+    // * isn't present.
+    // *
+    // * @param oid an object identifier representing a named parameters, if present.
+    // */
+    class function GetByName(const name: String): IECDomainParameters;
+      static; inline;
+    // /**
+    // * return the named curve name represented by the given object identifier.
+    // */
+    class function GetName(const oid: IDerObjectIdentifier): String;
+      static; inline;
+
+    class function GetOid(const name: String): IDerObjectIdentifier;
+      static; inline;
+
+    // /**
+    // * returns an enumeration containing the name strings for curves
+    // * contained in this structure.
+    // */
+    class property Names: TCryptoLibStringArray read GetNames;
+
+  end;
+
+implementation
+
+{ TECGost3410NamedCurves }
+
+class procedure TECGost3410NamedCurves.Boot;
+var
+  mod_p, mod_q: TBigInteger;
+  curve: IFPCurve;
+  ecParams: IECDomainParameters;
+begin
+
+  Fparameters := TDictionary<IDerObjectIdentifier,
+    IECDomainParameters>.Create();
+  FobjIds := TDictionary<String, IDerObjectIdentifier>.Create();
+  Fnames := TDictionary<IDerObjectIdentifier, String>.Create();
+
+  mod_p := TBigInteger.Create
+    ('115792089237316195423570985008687907853269984665640564039457584007913129639319');
+  mod_q := TBigInteger.Create
+    ('115792089237316195423570985008687907853073762908499243225378155805079068850323');
+
+  curve := TFpCurve.Create(mod_p, // p
+    TBigInteger.Create
+    ('115792089237316195423570985008687907853269984665640564039457584007913129639316'),
+    // a
+    TBigInteger.Create('166'), // b
+    mod_q, TBigInteger.One);
+
+  ecParams := TECDomainParameters.Create(curve,
+    curve.CreatePoint(TBigInteger.Create('1'), // x
+    TBigInteger.Create
+    ('64033881142927202683649881450433473985931760268884941288852745803908878638612')
+    ), // y
+    mod_q);
+
+  Fparameters.Add(TCryptoProObjectIdentifiers.GostR3410x2001CryptoProA,
+    ecParams);
+
+  mod_p := TBigInteger.Create
+    ('115792089237316195423570985008687907853269984665640564039457584007913129639319');
+  mod_q := TBigInteger.Create
+    ('115792089237316195423570985008687907853073762908499243225378155805079068850323');
+
+  curve := TFpCurve.Create(mod_p, // p
+    TBigInteger.Create
+    ('115792089237316195423570985008687907853269984665640564039457584007913129639316'),
+    TBigInteger.Create('166'), mod_q, TBigInteger.One);
+
+  ecParams := TECDomainParameters.Create(curve,
+    curve.CreatePoint(TBigInteger.Create('1'), // x
+    TBigInteger.Create
+    ('64033881142927202683649881450433473985931760268884941288852745803908878638612')
+    ), // y
+    mod_q);
+
+  Fparameters.Add(TCryptoProObjectIdentifiers.GostR3410x2001CryptoProXchA,
+    ecParams);
+
+  mod_p := TBigInteger.Create
+    ('57896044618658097711785492504343953926634992332820282019728792003956564823193');
+  // p
+  mod_q := TBigInteger.Create
+    ('57896044618658097711785492504343953927102133160255826820068844496087732066703');
+  // q
+
+  curve := TFpCurve.Create(mod_p, // p
+    TBigInteger.Create
+    ('57896044618658097711785492504343953926634992332820282019728792003956564823190'),
+    // a
+    TBigInteger.Create
+    ('28091019353058090096996979000309560759124368558014865957655842872397301267595'),
+    // b
+    mod_q, TBigInteger.One);
+
+  ecParams := TECDomainParameters.Create(curve,
+    curve.CreatePoint(TBigInteger.Create('1'), // x
+    TBigInteger.Create
+    ('28792665814854611296992347458380284135028636778229113005756334730996303888124')
+    ), // y
+    mod_q); // q
+
+  Fparameters.Add(TCryptoProObjectIdentifiers.GostR3410x2001CryptoProB,
+    ecParams);
+
+  mod_p := TBigInteger.Create
+    ('70390085352083305199547718019018437841079516630045180471284346843705633502619');
+  mod_q := TBigInteger.Create
+    ('70390085352083305199547718019018437840920882647164081035322601458352298396601');
+
+  curve := TFpCurve.Create(mod_p, // p
+    TBigInteger.Create
+    ('70390085352083305199547718019018437841079516630045180471284346843705633502616'),
+    TBigInteger.Create('32858'), mod_q, TBigInteger.One);
+
+  ecParams := TECDomainParameters.Create(curve,
+    curve.CreatePoint(TBigInteger.Create('0'),
+    TBigInteger.Create
+    ('29818893917731240733471273240314769927240550812383695689146495261604565990247')
+    ), mod_q);
+
+  Fparameters.Add(TCryptoProObjectIdentifiers.GostR3410x2001CryptoProXchB,
+    ecParams);
+
+  mod_p := TBigInteger.Create
+    ('70390085352083305199547718019018437841079516630045180471284346843705633502619');
+  // p
+  mod_q := TBigInteger.Create
+    ('70390085352083305199547718019018437840920882647164081035322601458352298396601');
+  // q
+  curve := TFpCurve.Create(mod_p, // p
+    TBigInteger.Create
+    ('70390085352083305199547718019018437841079516630045180471284346843705633502616'),
+    // a
+    TBigInteger.Create('32858'), // b
+    mod_q, TBigInteger.One);
+
+  ecParams := TECDomainParameters.Create(curve,
+    curve.CreatePoint(TBigInteger.Create('0'), // x
+    TBigInteger.Create
+    ('29818893917731240733471273240314769927240550812383695689146495261604565990247')
+    ), // y
+    mod_q); // q
+
+  Fparameters.Add(TCryptoProObjectIdentifiers.GostR3410x2001CryptoProC,
+    ecParams);
+
+  // GOST34.10 2012
+  mod_p := TBigInteger.Create
+    ('115792089237316195423570985008687907853269984665640564039457584007913129639319');
+  // p
+  mod_q := TBigInteger.Create
+    ('115792089237316195423570985008687907853073762908499243225378155805079068850323');
+  // q
+  curve := TFpCurve.Create(mod_p, // p
+    TBigInteger.Create
+    ('115792089237316195423570985008687907853269984665640564039457584007913129639316'),
+    // a
+    TBigInteger.Create('166'), // b
+    mod_q, TBigInteger.One);
+
+  ecParams := TECDomainParameters.Create(curve,
+    curve.CreatePoint(TBigInteger.Create('1'), // x
+    TBigInteger.Create
+    ('64033881142927202683649881450433473985931760268884941288852745803908878638612')
+    ), // y
+    mod_q); // q
+
+  Fparameters.Add(TRosstandartObjectIdentifiers.
+    id_tc26_gost_3410_12_256_paramSetA, ecParams);
+
+  mod_p := TBigInteger.Create
+    ('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7',
+    16); // p
+  mod_q := TBigInteger.Create
+    ('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275',
+    16); // q
+  curve := TFpCurve.Create(mod_p, // p
+    TBigInteger.Create
+    ('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4',
+    16), // a
+    TBigInteger.Create
+    ('E8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760',
+    16), // b
+    mod_q, TBigInteger.One);
+
+  ecParams := TECDomainParameters.Create(curve,
+    curve.CreatePoint(TBigInteger.Create
+    ('00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003'),
+    // x
+    TBigInteger.Create
+    ('7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4',
+    16)), // y
+    mod_q); // q
+
+  Fparameters.Add(TRosstandartObjectIdentifiers.
+    id_tc26_gost_3410_12_512_paramSetA, ecParams);
+
+  mod_p := TBigInteger.Create
+    ('8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006F',
+    16); // p
+  mod_q := TBigInteger.Create
+    ('800000000000000000000000000000000000000000000000000000000000000149A1EC142565A545ACFDB77BD9D40CFA8B996712101BEA0EC6346C54374F25BD',
+    16); // q
+  curve := TFpCurve.Create(mod_p, // p
+    TBigInteger.Create
+    ('8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006C',
+    16), // a
+    TBigInteger.Create
+    ('687D1B459DC841457E3E06CF6F5E2517B97C7D614AF138BCBF85DC806C4B289F3E965D2DB1416D217F8B276FAD1AB69C50F78BEE1FA3106EFB8CCBC7C5140116',
+    16), // b
+    mod_q, TBigInteger.One);
+
+  ecParams := TECDomainParameters.Create(curve,
+    curve.CreatePoint(TBigInteger.Create
+    ('00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002'),
+    // x
+    TBigInteger.Create
+    ('1A8F7EDA389B094C2C071E3647A8940F3C123B697578C213BE6DD9E6C8EC7335DCB228FD1EDF4A39152CBCAAF8C0398828041055F94CEEEC7E21340780FE41BD',
+    16)), // y
+    mod_q); // q
+
+  Fparameters.Add(TRosstandartObjectIdentifiers.
+    id_tc26_gost_3410_12_512_paramSetB, ecParams);
+
+  mod_p := TBigInteger.Create
+    ('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7',
+    16); // p
+  mod_q := TBigInteger.Create
+    ('3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC98CDBA46506AB004C33A9FF5147502CC8EDA9E7A769A12694623CEF47F023ED',
+    16); // q
+  curve := TFpCurve.Create(mod_p, // p
+    TBigInteger.Create
+    ('DC9203E514A721875485A529D2C722FB187BC8980EB866644DE41C68E143064546E861C0E2C9EDD92ADE71F46FCF50FF2AD97F951FDA9F2A2EB6546F39689BD3',
+    16), // a
+    TBigInteger.Create
+    ('B4C4EE28CEBC6C2C8AC12952CF37F16AC7EFB6A9F69F4B57FFDA2E4F0DE5ADE038CBC2FFF719D2C18DE0284B8BFEF3B52B8CC7A5F5BF0A3C8D2319A5312557E1',
+    16), // b
+    mod_q, TBigInteger.One);
+
+  ecParams := TECDomainParameters.Create(curve,
+    curve.CreatePoint(TBigInteger.Create
+    ('E2E31EDFC23DE7BDEBE241CE593EF5DE2295B7A9CBAEF021D385F7074CEA043AA27272A7AE602BF2A7B9033DB9ED3610C6FB85487EAE97AAC5BC7928C1950148',
+    16), // x
+    TBigInteger.Create
+    ('F5CE40D95B5EB899ABBCCFF5911CB8577939804D6527378B8C108C3D2090FF9BE18E2D33E3021ED2EF32D85822423B6304F726AA854BAE07D0396E9A9ADDC40F',
+    16)), // y
+    mod_q); // q
+
+  Fparameters.Add(TRosstandartObjectIdentifiers.
+    id_tc26_gost_3410_12_512_paramSetC, ecParams);
+
+  FobjIds.Add('GostR3410-2001-CryptoPro-A',
+    TCryptoProObjectIdentifiers.GostR3410x2001CryptoProA);
+  FobjIds.Add('GostR3410-2001-CryptoPro-B',
+    TCryptoProObjectIdentifiers.GostR3410x2001CryptoProB);
+  FobjIds.Add('GostR3410-2001-CryptoPro-C',
+    TCryptoProObjectIdentifiers.GostR3410x2001CryptoProC);
+  FobjIds.Add('GostR3410-2001-CryptoPro-XchA',
+    TCryptoProObjectIdentifiers.GostR3410x2001CryptoProXchA);
+  FobjIds.Add('GostR3410-2001-CryptoPro-XchB',
+    TCryptoProObjectIdentifiers.GostR3410x2001CryptoProXchB);
+  FobjIds.Add('Tc26-Gost-3410-12-256-paramSetA',
+    TRosstandartObjectIdentifiers.id_tc26_gost_3410_12_256_paramSetA);
+  FobjIds.Add('Tc26-Gost-3410-12-512-paramSetA',
+    TRosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetA);
+  FobjIds.Add('Tc26-Gost-3410-12-512-paramSetB',
+    TRosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetB);
+  FobjIds.Add('Tc26-Gost-3410-12-512-paramSetC',
+    TRosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetC);
+
+  Fnames.Add(TCryptoProObjectIdentifiers.GostR3410x2001CryptoProA,
+    'GostR3410-2001-CryptoPro-A');
+  Fnames.Add(TCryptoProObjectIdentifiers.GostR3410x2001CryptoProB,
+    'GostR3410-2001-CryptoPro-B');
+  Fnames.Add(TCryptoProObjectIdentifiers.GostR3410x2001CryptoProC,
+    'GostR3410-2001-CryptoPro-C');
+  Fnames.Add(TCryptoProObjectIdentifiers.GostR3410x2001CryptoProXchA,
+    'GostR3410-2001-CryptoPro-XchA');
+  Fnames.Add(TCryptoProObjectIdentifiers.GostR3410x2001CryptoProXchB,
+    'GostR3410-2001-CryptoPro-XchB');
+  Fnames.Add(TRosstandartObjectIdentifiers.id_tc26_gost_3410_12_256_paramSetA,
+    'Tc26-Gost-3410-12-256-paramSetA');
+  Fnames.Add(TRosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetA,
+    'Tc26-Gost-3410-12-512-paramSetA');
+  Fnames.Add(TRosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetB,
+    'Tc26-Gost-3410-12-512-paramSetB');
+  Fnames.Add(TRosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetC,
+    'Tc26-Gost-3410-12-512-paramSetC');
+end;
+
+class constructor TECGost3410NamedCurves.CreateECGost3410NamedCurves;
+begin
+  TECGost3410NamedCurves.Boot;
+end;
+
+class destructor TECGost3410NamedCurves.DestroyECGost3410NamedCurves;
+begin
+  Fparameters.Free;
+  FobjIds.Free;
+  Fnames.Free;
+end;
+
+class function TECGost3410NamedCurves.GetByName(const name: String)
+  : IECDomainParameters;
+var
+  oid: IDerObjectIdentifier;
+begin
+  if (FobjIds.TryGetValue(name, oid)) then
+  begin
+    Fparameters.TryGetValue(oid, Result);
+    Exit;
+  end;
+  Result := Nil;
+end;
+
+class function TECGost3410NamedCurves.GetByOid(const oid: IDerObjectIdentifier)
+  : IECDomainParameters;
+begin
+  Fparameters.TryGetValue(oid, Result);
+end;
+
+class function TECGost3410NamedCurves.GetName
+  (const oid: IDerObjectIdentifier): String;
+begin
+  Fnames.TryGetValue(oid, Result);
+end;
+
+class function TECGost3410NamedCurves.GetNames: TCryptoLibStringArray;
+begin
+  Result := Fnames.Values.ToArray();
+end;
+
+class function TECGost3410NamedCurves.GetOid(const name: String)
+  : IDerObjectIdentifier;
+begin
+  if (not(FobjIds.TryGetValue(name, Result))) then
+  begin
+    Result := Nil;
+  end;
+end;
+
+end.

+ 104 - 0
src/libraries/cryptolib4pascal/ClpECIESPublicKeyParser.pas

@@ -0,0 +1,104 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECIESPublicKeyParser;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpAsn1Objects,
+  ClpIECDomainParameters,
+  ClpECPublicKeyParameters,
+  ClpIKeyParser,
+  ClpIAsymmetricKeyParameter,
+  ClpIECIESPublicKeyParser,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SSenderPublicKeyInvalid = 'Sender''s Public Key Invalid.';
+  SSenderPublicKeyInvalidPointEncoding =
+    'Sender''s Public Key has Invalid Point Encoding "%x"';
+
+type
+  TECIESPublicKeyParser = class sealed(TInterfacedObject, IECIESPublicKeyParser,
+    IKeyParser)
+
+  strict private
+  var
+    FecParams: IECDomainParameters;
+
+  public
+    function ReadKey(const Stream: TStream): IAsymmetricKeyParameter;
+    constructor Create(const ecParams: IECDomainParameters);
+
+  end;
+
+implementation
+
+{ TECIESPublicKeyParser }
+
+constructor TECIESPublicKeyParser.Create(const ecParams: IECDomainParameters);
+begin
+  Inherited Create();
+  FecParams := ecParams;
+end;
+
+function TECIESPublicKeyParser.ReadKey(const Stream: TStream)
+  : IAsymmetricKeyParameter;
+var
+  v: TCryptoLibByteArray;
+  first: Int32;
+begin
+  first := Stream.ReadByte;
+  // Decode the public ephemeral key
+  case first of
+    $00: // infinity
+      begin
+        raise EIOCryptoLibException.CreateRes(@SSenderPublicKeyInvalid);
+      end;
+
+    $02, // compressed
+    $03: // Byte length calculated as in ECPoint.getEncoded();
+      begin
+        System.SetLength(v, 1 + (FecParams.Curve.FieldSize + 7) div 8);
+      end;
+
+    $04, // uncompressed or
+    $06, // hybrid
+    $07: // Byte length calculated as in ECPoint.getEncoded();
+      begin
+        System.SetLength(v, 1 + (2 * ((FecParams.Curve.FieldSize + 7) div 8)));
+      end
+  else
+    begin
+      raise EIOCryptoLibException.CreateResFmt
+        (@SSenderPublicKeyInvalidPointEncoding, [first]);
+    end;
+
+  end;
+
+  v[0] := Byte(first);
+  TStreamUtils.ReadFully(Stream, v, 1, System.length(v) - 1);
+
+  result := TECPublicKeyParameters.Create(FecParams.Curve.DecodePoint(v),
+    FecParams);
+end;
+
+end.

+ 85 - 0
src/libraries/cryptolib4pascal/ClpECKeyGenerationParameters.pas

@@ -0,0 +1,85 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECKeyGenerationParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpBigInteger,
+  ClpISecureRandom,
+  ClpIECKeyGenerationParameters,
+  ClpIECDomainParameters,
+  ClpIAsn1Objects,
+  ClpKeyGenerationParameters;
+
+type
+  TECKeyGenerationParameters = class sealed(TKeyGenerationParameters,
+    IECKeyGenerationParameters)
+
+  strict private
+  var
+    FdomainParams: IECDomainParameters;
+    FpublicKeyParamSet: IDerObjectIdentifier;
+
+    function GetDomainParameters: IECDomainParameters;
+    function GetPublicKeyParamSet: IDerObjectIdentifier;
+
+  public
+    constructor Create(const domainParameters: IECDomainParameters;
+      const random: ISecureRandom); overload;
+    constructor Create(const publicKeyParamSet: IDerObjectIdentifier;
+      const random: ISecureRandom); overload;
+    property domainParameters: IECDomainParameters read GetDomainParameters;
+    property publicKeyParamSet: IDerObjectIdentifier read GetPublicKeyParamSet;
+
+  end;
+
+implementation
+
+uses
+  ClpECKeyParameters; // included here to avoid circular dependency :)
+
+{ TECKeyGenerationParameters }
+
+constructor TECKeyGenerationParameters.Create(const domainParameters
+  : IECDomainParameters; const random: ISecureRandom);
+begin
+  Inherited Create(random, domainParameters.N.BitLength);
+  FdomainParams := domainParameters;
+end;
+
+constructor TECKeyGenerationParameters.Create(const publicKeyParamSet
+  : IDerObjectIdentifier; const random: ISecureRandom);
+begin
+  Create(TECKeyParameters.LookupParameters(publicKeyParamSet), random);
+  FpublicKeyParamSet := publicKeyParamSet;
+end;
+
+function TECKeyGenerationParameters.GetDomainParameters: IECDomainParameters;
+begin
+  Result := FdomainParams;
+end;
+
+function TECKeyGenerationParameters.GetPublicKeyParamSet: IDerObjectIdentifier;
+begin
+  Result := FpublicKeyParamSet;
+end;
+
+end.

+ 241 - 0
src/libraries/cryptolib4pascal/ClpECKeyPairGenerator.pas

@@ -0,0 +1,241 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECKeyPairGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpBigInteger,
+  ClpBits,
+  ClpCryptoLibTypes,
+  ClpECKeyParameters,
+  ClpECAlgorithms,
+  ClpIECKeyPairGenerator,
+  ClpIAsn1Objects,
+  ClpIKeyGenerationParameters,
+  ClpIECKeyGenerationParameters,
+  ClpECDomainParameters,
+  ClpIECDomainParameters,
+  ClpIECC,
+  ClpIFixedPointCombMultiplier,
+  ClpSecObjectIdentifiers,
+  ClpCustomNamedCurves,
+  ClpECNamedCurveTable,
+  ClpX9ObjectIdentifiers,
+  ClpIX9ECParameters,
+  ClpAsymmetricCipherKeyPair,
+  ClpIAsymmetricCipherKeyPair,
+  ClpECPublicKeyParameters,
+  ClpIECPublicKeyParameters,
+  ClpECPrivateKeyParameters,
+  ClpIECPrivateKeyParameters,
+  ClpFixedPointCombMultiplier,
+  ClpSecureRandom,
+  ClpISecureRandom,
+  ClpIAsymmetricCipherKeyPairGenerator;
+
+resourcestring
+  SAlgorithmNil = 'Algorithm Cannot be Empty';
+  SInvalidKeySize = 'Unknown Key Size "%d"';
+
+type
+  TECKeyPairGenerator = class sealed(TInterfacedObject, IECKeyPairGenerator,
+    IAsymmetricCipherKeyPairGenerator)
+
+  strict private
+  var
+    Falgorithm: String;
+    Fparameters: IECDomainParameters;
+    FpublicKeyParamSet: IDerObjectIdentifier;
+    Frandom: ISecureRandom;
+
+  strict protected
+
+    function CreateBasePointMultiplier(): IECMultiplier; virtual;
+
+  public
+    constructor Create(); overload;
+    constructor Create(const algorithm: String); overload;
+
+    procedure Init(const parameters: IKeyGenerationParameters);
+    // /**
+    // * Given the domain parameters this routine generates an EC key
+    // * pair in accordance with X9.62 section 5.2.1 pages 26, 27.
+    // */
+    function GenerateKeyPair(): IAsymmetricCipherKeyPair;
+
+    class function FindECCurveByOid(const oid: IDerObjectIdentifier)
+      : IX9ECParameters; static;
+
+    class function GetCorrespondingPublicKey(const privKey
+      : IECPrivateKeyParameters): IECPublicKeyParameters; static;
+
+  end;
+
+implementation
+
+{ TECKeyPairGenerator }
+
+constructor TECKeyPairGenerator.Create;
+begin
+  Create('EC');
+end;
+
+constructor TECKeyPairGenerator.Create(const algorithm: String);
+begin
+  if (algorithm = '') then
+    raise EArgumentNilCryptoLibException.CreateRes(@SAlgorithmNil);
+
+  Falgorithm := TECKeyParameters.VerifyAlgorithmName(algorithm);
+end;
+
+function TECKeyPairGenerator.CreateBasePointMultiplier: IECMultiplier;
+begin
+  result := TFixedPointCombMultiplier.Create();
+end;
+
+class function TECKeyPairGenerator.FindECCurveByOid
+  (const oid: IDerObjectIdentifier): IX9ECParameters;
+var
+  ecP: IX9ECParameters;
+begin
+  // TODO ECGost3410NamedCurves support (returns ECDomainParameters though)
+
+  ecP := TCustomNamedCurves.GetByOid(oid);
+  if (ecP = Nil) then
+  begin
+    ecP := TECNamedCurveTable.GetByOid(oid);
+  end;
+
+  result := ecP;
+end;
+
+function TECKeyPairGenerator.GenerateKeyPair: IAsymmetricCipherKeyPair;
+var
+  n, d: TBigInteger;
+  minWeight: Int32;
+  q: IECPoint;
+begin
+  n := Fparameters.n;
+  minWeight := TBits.Asr32(n.BitLength, 2);
+
+  while (true) do
+  begin
+    d := TBigInteger.Create(n.BitLength, Frandom);
+
+    if ((d.CompareTo(TBigInteger.Two) < 0) or (d.CompareTo(n) >= 0)) then
+      continue;
+
+    if (TWNafUtilities.GetNafWeight(d) < minWeight) then
+    begin
+      continue;
+    end;
+
+    break;
+  end;
+
+  q := CreateBasePointMultiplier().Multiply(Fparameters.G, d);
+
+  if (FpublicKeyParamSet <> Nil) then
+  begin
+    result := TAsymmetricCipherKeyPair.Create
+      (TECPublicKeyParameters.Create(Falgorithm, q, FpublicKeyParamSet)
+      as IECPublicKeyParameters, TECPrivateKeyParameters.Create(Falgorithm, d,
+      FpublicKeyParamSet) as IECPrivateKeyParameters);
+    Exit;
+  end;
+
+  result := TAsymmetricCipherKeyPair.Create
+    (TECPublicKeyParameters.Create(Falgorithm, q, Fparameters)
+    as IECPublicKeyParameters, TECPrivateKeyParameters.Create(Falgorithm, d,
+    Fparameters) as IECPrivateKeyParameters);
+end;
+
+class function TECKeyPairGenerator.GetCorrespondingPublicKey
+  (const privKey: IECPrivateKeyParameters): IECPublicKeyParameters;
+var
+  ec: IECDomainParameters;
+  q: IECPoint;
+begin
+  ec := privKey.parameters;
+  q := (TFixedPointCombMultiplier.Create() as IFixedPointCombMultiplier)
+    .Multiply(ec.G, privKey.d);
+
+  if (privKey.publicKeyParamSet <> Nil) then
+  begin
+    result := TECPublicKeyParameters.Create(privKey.AlgorithmName, q,
+      privKey.publicKeyParamSet);
+    Exit;
+  end;
+
+  result := TECPublicKeyParameters.Create(privKey.AlgorithmName, q, ec);
+end;
+
+procedure TECKeyPairGenerator.Init(const parameters: IKeyGenerationParameters);
+var
+  ecP: IECKeyGenerationParameters;
+  ecps: IX9ECParameters;
+  oid: IDerObjectIdentifier;
+begin
+  if (Supports(parameters, IECKeyGenerationParameters, ecP)) then
+  begin
+    FpublicKeyParamSet := ecP.publicKeyParamSet;
+    Fparameters := ecP.DomainParameters;
+  end
+  else
+  begin
+    case parameters.Strength of
+      192:
+        oid := TX9ObjectIdentifiers.Prime192v1;
+      224:
+        oid := TSecObjectIdentifiers.SecP224r1;
+      239:
+        oid := TX9ObjectIdentifiers.Prime239v1;
+      256:
+        oid := TX9ObjectIdentifiers.Prime256v1;
+      384:
+        oid := TSecObjectIdentifiers.SecP384r1;
+      521:
+        oid := TSecObjectIdentifiers.SecP521r1;
+    else
+      raise EInvalidParameterCryptoLibException.CreateResFmt(@SInvalidKeySize,
+        [parameters.Strength]);
+
+    end;
+
+    ecps := FindECCurveByOid(oid);
+
+    FpublicKeyParamSet := oid;
+    Fparameters := TECDomainParameters.Create(ecps.Curve, ecps.G, ecps.n,
+      ecps.H, ecps.GetSeed());
+
+  end;
+
+  Frandom := parameters.random;
+
+  if (Frandom = Nil) then
+  begin
+    Frandom := TSecureRandom.Create();
+  end;
+
+end;
+
+end.

+ 234 - 0
src/libraries/cryptolib4pascal/ClpECKeyParameters.pas

@@ -0,0 +1,234 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECKeyParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+
+  SysUtils,
+  ClpECGost3410NamedCurves,
+  ClpCryptoLibTypes,
+  ClpECDomainParameters,
+  ClpIECKeyParameters,
+  ClpIX9ECParameters,
+  ClpIECDomainParameters,
+  ClpIAsn1Objects,
+  ClpIECKeyGenerationParameters,
+  ClpAsymmetricKeyParameter,
+  ClpECKeyGenerationParameters,
+  ClpISecureRandom;
+
+resourcestring
+  SAlgorithmNil = 'Algorithm Cannot be Empty';
+  SParameterNil = 'Parameter Cannot be Nil';
+  SPublicKeyParamSetNil = 'PublicKeyParamSet Cannot be Nil';
+  SInvalidPublicKeyParamSetNil =
+    'OID Is Not a Valid Public Key Parameter Set  "PublicKeyParamSet"';
+  SUnRecognizedAlgorithm = 'Unrecognised Algorithm: " %s, "Algorithm';
+
+type
+  TECKeyParameters = class abstract(TAsymmetricKeyParameter, IECKeyParameters)
+
+  strict private
+
+  const
+
+    Falgorithms: array [0 .. 6] of String = ('EC', 'ECDSA', 'ECDH', 'ECDHC',
+      'ECGOST3410', 'ECMQV', 'ECSCHNORR');
+
+  var
+    Falgorithm: String;
+    Fparameters: IECDomainParameters;
+    FpublicKeyParamSet: IDerObjectIdentifier;
+
+  strict protected
+
+    constructor Create(const algorithm: String; isPrivate: Boolean;
+      const parameters: IECDomainParameters); overload;
+
+    constructor Create(const algorithm: String; isPrivate: Boolean;
+      const publicKeyParamSet: IDerObjectIdentifier); overload;
+
+    function CreateKeyGenerationParameters(const random: ISecureRandom)
+      : IECKeyGenerationParameters; inline;
+
+    function GetAlgorithmName: String; inline;
+    function GetPublicKeyParamSet: IDerObjectIdentifier; inline;
+    function GetParameters: IECDomainParameters; inline;
+
+    function Equals(const other: IECKeyParameters): Boolean; reintroduce; overload;
+
+  public
+    class function VerifyAlgorithmName(const algorithm: String): String; static;
+    class function LookupParameters(const publicKeyParamSet
+      : IDerObjectIdentifier): IECDomainParameters; static;
+    property AlgorithmName: String read GetAlgorithmName;
+    property parameters: IECDomainParameters read GetParameters;
+    property publicKeyParamSet: IDerObjectIdentifier read GetPublicKeyParamSet;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+  end;
+
+implementation
+
+uses
+  ClpECKeyPairGenerator; // included here to avoid circular dependency :)
+
+{ TECKeyParameters }
+
+function TECKeyParameters.GetPublicKeyParamSet: IDerObjectIdentifier;
+begin
+  result := FpublicKeyParamSet;
+end;
+
+function TECKeyParameters.GetParameters: IECDomainParameters;
+begin
+  result := Fparameters;
+end;
+
+class function TECKeyParameters.LookupParameters(const publicKeyParamSet
+  : IDerObjectIdentifier): IECDomainParameters;
+var
+  p: IECDomainParameters;
+  x9: IX9ECParameters;
+
+begin
+  if publicKeyParamSet = Nil then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SPublicKeyParamSetNil);
+  end;
+
+  p := TECGost3410NamedCurves.GetByOid(publicKeyParamSet);
+
+  if (p = Nil) then
+  begin
+    x9 := TECKeyPairGenerator.FindECCurveByOid(publicKeyParamSet);
+
+    if (x9 = Nil) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes
+        (@SInvalidPublicKeyParamSetNil);
+
+    end;
+
+    p := TECDomainParameters.Create(x9.Curve, x9.G, x9.N, x9.H, x9.GetSeed());
+  end;
+
+  result := p;
+
+end;
+
+class function TECKeyParameters.VerifyAlgorithmName(const algorithm
+  : String): String;
+var
+  upper: String;
+  i: Int32;
+  found: Boolean;
+begin
+  upper := UpperCase(algorithm);
+  found := false;
+  for i := System.Low(Falgorithms) to System.High(Falgorithms) do
+  begin
+    if (Falgorithms[i] = algorithm) then
+    begin
+      found := true;
+      break;
+    end;
+  end;
+
+  if (not found) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SUnRecognizedAlgorithm,
+      [algorithm]);
+  end;
+  result := upper;
+end;
+
+constructor TECKeyParameters.Create(const algorithm: String; isPrivate: Boolean;
+  const parameters: IECDomainParameters);
+begin
+  Inherited Create(isPrivate);
+  if (algorithm = '') then
+    raise EArgumentNilCryptoLibException.CreateRes(@SAlgorithmNil);
+
+  if (parameters = Nil) then
+    raise EArgumentNilCryptoLibException.CreateRes(@SParameterNil);
+
+  Falgorithm := VerifyAlgorithmName(algorithm);
+  Fparameters := parameters;
+end;
+
+constructor TECKeyParameters.Create(const algorithm: String; isPrivate: Boolean;
+  const publicKeyParamSet: IDerObjectIdentifier);
+begin
+  Inherited Create(isPrivate);
+
+  if (algorithm = '') then
+    raise EArgumentNilCryptoLibException.CreateRes(@SAlgorithmNil);
+
+  if (publicKeyParamSet = Nil) then
+    raise EArgumentNilCryptoLibException.CreateRes(@SPublicKeyParamSetNil);
+
+  Falgorithm := VerifyAlgorithmName(algorithm);
+  Fparameters := LookupParameters(publicKeyParamSet);
+  FpublicKeyParamSet := publicKeyParamSet;
+end;
+
+function TECKeyParameters.CreateKeyGenerationParameters
+  (const random: ISecureRandom): IECKeyGenerationParameters;
+begin
+  if (publicKeyParamSet <> Nil) then
+  begin
+    result := TECKeyGenerationParameters.Create(publicKeyParamSet, random);
+  end;
+
+  result := TECKeyGenerationParameters.Create(parameters, random);
+end;
+
+function TECKeyParameters.Equals(const other: IECKeyParameters): Boolean;
+begin
+  if (other = Self as IECKeyParameters) then
+  begin
+    result := true;
+    Exit;
+  end;
+
+  if (other = Nil) then
+  begin
+    result := false;
+    Exit;
+  end;
+  result := Fparameters.Equals(other.parameters) and (inherited Equals(other));
+end;
+
+function TECKeyParameters.GetAlgorithmName: String;
+begin
+  result := Falgorithm;
+end;
+
+function TECKeyParameters.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := Fparameters.GetHashCode() xor (inherited GetHashCode());
+end;
+
+end.

+ 295 - 0
src/libraries/cryptolib4pascal/ClpECNRSigner.pas

@@ -0,0 +1,295 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECNRSigner;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpIDsaExt,
+  ClpIECC,
+  ClpIECNRSigner,
+  ClpBigInteger,
+  ClpISecureRandom,
+  ClpIECKeyParameters,
+  ClpIParametersWithRandom,
+  ClpICipherParameters,
+  ClpIECKeyPairGenerator,
+  ClpECKeyPairGenerator,
+  ClpECKeyGenerationParameters,
+  ClpIECKeyGenerationParameters,
+  ClpIECPrivateKeyParameters,
+  ClpIECPublicKeyParameters,
+  ClpIAsymmetricCipherKeyPair,
+  ClpSecureRandom,
+  ClpECAlgorithms,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SECPublicKeyNotFound = 'EC Public Key Required for Verification';
+  SECPrivateKeyNotFound = 'EC Private Key Required for Signing';
+  SNotInitializedForSigning = 'Not Initialised For Signing';
+  SNotInitializedForVerifying = 'Not Initialised For Verifying';
+  SInputTooLargeForECNRKey = 'Input Too Large For ECNR Key.';
+
+type
+
+  /// <summary>
+  /// EC-NR as described in IEEE 1363-2000
+  /// </summary>
+  TECNRSigner = class sealed(TInterfacedObject, IDsaExt, IECNRSigner)
+
+  strict private
+  var
+    FforSigning: Boolean;
+    Fkey: IECKeyParameters;
+    Frandom: ISecureRandom;
+
+    function GetAlgorithmName: String; virtual;
+    function GetOrder: TBigInteger; virtual;
+
+  public
+
+    property Order: TBigInteger read GetOrder;
+    property AlgorithmName: String read GetAlgorithmName;
+
+    procedure Init(forSigning: Boolean;
+      const parameters: ICipherParameters); virtual;
+
+    /// <summary>
+    /// <para>
+    /// Section 7.2.5 ECSP-NR, pg 34
+    /// </para>
+    /// <para>
+    /// generate a signature for the given message using the key we were <br />
+    /// initialised with. Generally, the order of the curve should be at <br />
+    /// least as long as the hash of the message of interest, and with <br />
+    /// ECNR it *must* be at least as long.
+    /// </para>
+    /// </summary>
+    /// <param name="&amp;message">
+    /// the digest to be signed.
+    /// </param>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if the digest is longer than the key allows
+    /// </exception>
+    function GenerateSignature(const &message: TCryptoLibByteArray)
+      : TCryptoLibGenericArray<TBigInteger>; virtual;
+
+    /// <summary>
+    /// <para>
+    /// Section 7.2.6 ECVP-NR, pg 35
+    /// </para>
+    /// <para>
+    /// return true if the value r and s represent a signature for the <br />
+    /// message passed in. Generally, the order of the curve should be at
+    /// <br />least as long as the hash of the message of interest, and
+    /// with <br />ECNR, it *must* be at least as long. But just in case
+    /// the signer <br />applied mod(n) to the longer digest, this
+    /// implementation will <br />apply mod(n) during verification.
+    /// </para>
+    /// </summary>
+    /// <param name="&amp;message">
+    /// the digest to be verified.
+    /// </param>
+    /// <param name="r">
+    /// the r value of the signature.
+    /// </param>
+    /// <param name="s">
+    /// the s value of the signature.
+    /// </param>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if the digest is longer than the key allows
+    /// </exception>
+    function VerifySignature(const &message: TCryptoLibByteArray;
+      const r, s: TBigInteger): Boolean;
+
+  end;
+
+implementation
+
+{ TECNRSigner }
+
+function TECNRSigner.GenerateSignature(const &message: TCryptoLibByteArray)
+  : TCryptoLibGenericArray<TBigInteger>;
+var
+  n, e, r, s, Vx, x, u: TBigInteger;
+  nBitLength, eBitLength: Int32;
+  privKey: IECPrivateKeyParameters;
+  tempPair: IAsymmetricCipherKeyPair;
+  keyGen: IECKeyPairGenerator;
+  V: IECPublicKeyParameters;
+begin
+  if (not FforSigning) then
+  begin
+    // not properly initialized... deal with it
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SNotInitializedForSigning);
+  end;
+
+  n := Order;
+  nBitLength := n.BitLength;
+
+  e := TBigInteger.Create(1, &message);
+  eBitLength := e.BitLength;
+
+  privKey := Fkey as IECPrivateKeyParameters;
+
+  if (eBitLength > nBitLength) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SInputTooLargeForECNRKey);
+  end;
+
+  repeat // generate r
+    // generate another, but very temporary, key pair using
+    // the same EC parameters
+    keyGen := TECKeyPairGenerator.Create();
+
+    keyGen.Init(TECKeyGenerationParameters.Create(privKey.parameters, Frandom)
+      as IECKeyGenerationParameters);
+
+    tempPair := keyGen.GenerateKeyPair();
+
+    // BigInteger Vx := tempPair.getPublic().getW().getAffineX();
+    V := tempPair.Public as IECPublicKeyParameters; // get temp's public key
+    Vx := V.Q.AffineXCoord.ToBigInteger(); // get the point's x coordinate
+
+    r := Vx.Add(e).&Mod(n);
+  until (not(r.SignValue = 0));
+
+  // generate s
+  x := privKey.D; // private key value
+  u := (tempPair.Private as IECPrivateKeyParameters).D;
+  // temp's private key value
+  s := u.Subtract(r.Multiply(x)).&Mod(n);
+
+  result := TCryptoLibGenericArray<TBigInteger>.Create(r, s);
+end;
+
+function TECNRSigner.GetAlgorithmName: String;
+begin
+  result := 'ECNR';
+end;
+
+function TECNRSigner.GetOrder: TBigInteger;
+begin
+  result := Fkey.parameters.n;
+end;
+
+procedure TECNRSigner.Init(forSigning: Boolean;
+  const parameters: ICipherParameters);
+var
+  rParam: IParametersWithRandom;
+  Lparameters: ICipherParameters;
+begin
+  FforSigning := forSigning;
+  Lparameters := parameters;
+  if (forSigning) then
+  begin
+
+    if (Supports(Lparameters, IParametersWithRandom, rParam)) then
+    begin
+      Frandom := rParam.random;
+      Lparameters := rParam.parameters;
+    end
+    else
+    begin
+      Frandom := TSecureRandom.Create();
+    end;
+
+    if (not(Supports(Lparameters, IECPrivateKeyParameters))) then
+    begin
+      raise EInvalidKeyCryptoLibException.CreateRes(@SECPrivateKeyNotFound);
+    end;
+
+    Fkey := Lparameters as IECPrivateKeyParameters;
+  end
+  else
+  begin
+    if (not(Supports(Lparameters, IECPublicKeyParameters))) then
+    begin
+      raise EInvalidKeyCryptoLibException.CreateRes(@SECPublicKeyNotFound);
+    end;
+
+    Fkey := Lparameters as IECPublicKeyParameters;
+  end;
+end;
+
+function TECNRSigner.VerifySignature(const &message: TCryptoLibByteArray;
+  const r, s: TBigInteger): Boolean;
+var
+  pubKey: IECPublicKeyParameters;
+  n, e, x, t: TBigInteger;
+  nBitLength, eBitLength: Int32;
+  G, W, P: IECPoint;
+begin
+  if (FforSigning) then
+  begin
+    // not properly initialized... deal with it
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SNotInitializedForVerifying);
+  end;
+
+  pubKey := Fkey as IECPublicKeyParameters;
+  n := pubKey.parameters.n;
+  nBitLength := n.BitLength;
+
+  e := TBigInteger.Create(1, &message);
+  eBitLength := e.BitLength;
+
+  if (eBitLength > nBitLength) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SInputTooLargeForECNRKey);
+  end;
+
+  // r in the range [1,n-1]
+  if ((r.CompareTo(TBigInteger.One) < 0) or (r.CompareTo(n) >= 0)) then
+  begin
+    result := false;
+    Exit;
+  end;
+
+  // s in the range [0,n-1]           NB: ECNR spec says 0
+  if ((s.CompareTo(TBigInteger.Zero) < 0) or (s.CompareTo(n) >= 0)) then
+  begin
+    result := false;
+    Exit;
+  end;
+
+  // compute P = sG + rW
+
+  G := pubKey.parameters.G;
+  W := pubKey.Q;
+  // calculate P using ECAlgorithms Math
+  P := TECAlgorithms.SumOfTwoMultiplies(G, s, W, r).Normalize();
+
+  if (P.IsInfinity) then
+  begin
+    result := false;
+    Exit;
+  end;
+
+  x := P.AffineXCoord.ToBigInteger();
+  t := r.Subtract(x).&Mod(n);
+
+  result := t.Equals(e);
+end;
+
+end.

+ 190 - 0
src/libraries/cryptolib4pascal/ClpECNamedCurveTable.pas

@@ -0,0 +1,190 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECNamedCurveTable;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Generics.Collections,
+  ClpCryptoLibTypes,
+  // ClpX962NamedCurves,
+  // ClpECGost3410NamedCurves,
+  // ClpX9ECParameters,
+  ClpSecNamedCurves,
+  ClpNistNamedCurves,
+  // ClpIECDomainParameters,
+  ClpIAsn1Objects,
+  ClpIX9ECParameters;
+
+type
+  /// <summary>
+  /// A general class that reads all X9.62 style EC curve tables.
+  /// </summary>
+  TECNamedCurveTable = class sealed(TObject)
+
+  strict private
+
+    class function GetNames: TCryptoLibStringArray; static;
+    // class function FromDomainParameters(const dp: IECDomainParameters)
+    // : IX9ECParameters; static; inline;
+
+  public
+    // /**
+    // * return a X9ECParameters object representing the passed in named
+    // * curve. The routine returns null if the curve is not present.
+    // *
+    // * @param name the name of the curve requested
+    // * @return an X9ECParameters object or null if the curve is not available.
+    // */
+    class function GetByName(const name: String): IX9ECParameters; static;
+
+    class function GetName(const oid: IDerObjectIdentifier): String; static;
+    // /**
+    // * return the object identifier signified by the passed in name. Null
+    // * if there is no object identifier associated with name.
+    // *
+    // * @return the object identifier associated with name, if present.
+    // */
+    class function GetOid(const name: String): IDerObjectIdentifier; static;
+
+    // /**
+    // * return a X9ECParameters object representing the passed in named
+    // * curve.
+    // *
+    // * @param oid the object id of the curve requested
+    // * @return an X9ECParameters object or null if the curve is not available.
+    // */
+
+    class function GetByOid(const oid: IDerObjectIdentifier)
+      : IX9ECParameters; static;
+
+    // /**
+    // * return an enumeration of the names of the available curves.
+    // *
+    // * @return an enumeration of the names of the available curves.
+    // */
+    class property Names: TCryptoLibStringArray read GetNames;
+
+  end;
+
+implementation
+
+{ TECNamedCurveTable }
+
+// class function TECNamedCurveTable.FromDomainParameters
+// (const dp: IECDomainParameters): IX9ECParameters;
+// begin
+// if dp = Nil then
+// begin
+// result := Nil;
+// end
+// else
+// begin
+// result := TX9ECParameters.Create(dp.Curve, dp.G, dp.N, dp.H, dp.GetSeed())
+// end;
+//
+// end;
+
+class function TECNamedCurveTable.GetByName(const name: String)
+  : IX9ECParameters;
+var
+  ecP: IX9ECParameters;
+begin
+  // ecP := TX962NamedCurves.GetByName(name);
+  // if (ecP = Nil) then
+  // begin
+  ecP := TSecNamedCurves.GetByName(name);
+  // end;
+
+  if (ecP = Nil) then
+  begin
+    ecP := TNistNamedCurves.GetByName(name);
+  end;
+
+  result := ecP;
+end;
+
+class function TECNamedCurveTable.GetByOid(const oid: IDerObjectIdentifier)
+  : IX9ECParameters;
+var
+  ecP: IX9ECParameters;
+begin
+  // ecP := TX962NamedCurves.GetByOid(oid);
+  // if (ecP = Nil) then
+  // begin
+  ecP := TSecNamedCurves.GetByOid(oid);
+  // end;
+  // NOTE: All the NIST curves are currently from SEC, so no point in redundant OID lookup
+  result := ecP;
+end;
+
+class function TECNamedCurveTable.GetName
+  (const oid: IDerObjectIdentifier): String;
+var
+  name: String;
+begin
+  // name := TX962NamedCurves.GetName(oid);
+  // if (name = '') then
+  // begin
+  name := TSecNamedCurves.GetName(oid);
+  // end;
+
+  if (name = '') then
+  begin
+    name := TNistNamedCurves.GetName(oid);
+  end;
+  result := name;
+end;
+
+class function TECNamedCurveTable.GetOid(const name: String)
+  : IDerObjectIdentifier;
+var
+  oid: IDerObjectIdentifier;
+begin
+  // oid := TX962NamedCurves.GetOid(name);
+  // if (oid = Nil) then
+  // begin
+  oid := TSecNamedCurves.GetOid(name);
+  // end;
+
+  if (oid = Nil) then
+  begin
+    oid := TNistNamedCurves.GetOid(name);
+  end;
+
+  result := oid;
+end;
+
+class function TECNamedCurveTable.GetNames: TCryptoLibStringArray;
+var
+  temp: TList<String>;
+begin
+  temp := TList<String>.Create();
+  try
+    temp.AddRange(TSecNamedCurves.Names);
+    temp.AddRange(TNistNamedCurves.Names);
+    result := temp.ToArray;
+  finally
+    temp.Free;
+  end;
+
+end;
+
+end.

+ 121 - 0
src/libraries/cryptolib4pascal/ClpECPrivateKeyParameters.pas

@@ -0,0 +1,121 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECPrivateKeyParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpBigInteger,
+  ClpCryptoLibTypes,
+  ClpECKeyParameters,
+  ClpIECPrivateKeyParameters,
+  ClpIAsn1Objects,
+  ClpIECDomainParameters;
+
+resourcestring
+  SBigIntegerNotInitialized = 'BigInteger Not Initialized "%s"';
+
+type
+  TECPrivateKeyParameters = class sealed(TECKeyParameters,
+    IECPrivateKeyParameters)
+
+  strict private
+  var
+    Fd: TBigInteger;
+
+    function GetD: TBigInteger; inline;
+
+  public
+    constructor Create(const d: TBigInteger;
+      const parameters: IECDomainParameters); overload;
+
+    constructor Create(const algorithm: String; const d: TBigInteger;
+      const parameters: IECDomainParameters); overload;
+
+    constructor Create(const algorithm: String; const d: TBigInteger;
+      const publicKeyParamSet: IDerObjectIdentifier); overload;
+
+    property d: TBigInteger read GetD;
+
+    function Equals(const other: IECPrivateKeyParameters): Boolean; reintroduce; overload;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+  end;
+
+implementation
+
+{ TECPrivateKeyParameters }
+
+function TECPrivateKeyParameters.GetD: TBigInteger;
+begin
+  result := Fd;
+end;
+
+constructor TECPrivateKeyParameters.Create(const d: TBigInteger;
+  const parameters: IECDomainParameters);
+begin
+  Create('EC', d, parameters);
+end;
+
+constructor TECPrivateKeyParameters.Create(const algorithm: String;
+  const d: TBigInteger; const parameters: IECDomainParameters);
+begin
+  Inherited Create(algorithm, true, parameters);
+  if (not(d.IsInitialized)) then
+    raise EArgumentNilCryptoLibException.CreateResFmt
+      (@SBigIntegerNotInitialized, ['d']);
+  Fd := d;
+end;
+
+constructor TECPrivateKeyParameters.Create(const algorithm: String;
+  const d: TBigInteger; const publicKeyParamSet: IDerObjectIdentifier);
+begin
+  Inherited Create(algorithm, true, publicKeyParamSet);
+  if (not(d.IsInitialized)) then
+    raise EArgumentNilCryptoLibException.CreateResFmt
+      (@SBigIntegerNotInitialized, ['d']);
+  Fd := d;
+end;
+
+function TECPrivateKeyParameters.Equals(const other
+  : IECPrivateKeyParameters): Boolean;
+begin
+  if (other = Self as IECPrivateKeyParameters) then
+  begin
+    result := true;
+    Exit;
+  end;
+
+  if (other = Nil) then
+  begin
+    result := false;
+    Exit;
+  end;
+  result := d.Equals(other.d) and (inherited Equals(other));
+end;
+
+function TECPrivateKeyParameters.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := d.GetHashCode() xor (inherited GetHashCode());
+end;
+
+end.

+ 129 - 0
src/libraries/cryptolib4pascal/ClpECPublicKeyParameters.pas

@@ -0,0 +1,129 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECPublicKeyParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpCryptoLibTypes,
+  ClpECDomainParameters,
+  ClpBigInteger,
+  ClpIECC,
+  ClpIECPublicKeyParameters,
+  ClpIAsn1Objects,
+  ClpIECDomainParameters,
+  ClpECKeyParameters;
+
+resourcestring
+  SQNil = 'Q Cannot be Nil';
+
+type
+  TECPublicKeyParameters = class sealed(TECKeyParameters,
+    IECPublicKeyParameters)
+
+  strict private
+  var
+    Fq: IECPoint;
+
+    function GetQ: IECPoint; inline;
+
+  public
+    constructor Create(const q: IECPoint;
+      const parameters: IECDomainParameters); overload;
+
+    constructor Create(const algorithm: String; const q: IECPoint;
+      const parameters: IECDomainParameters); overload;
+
+    constructor Create(const algorithm: String; const q: IECPoint;
+      const publicKeyParamSet: IDerObjectIdentifier); overload;
+
+    property q: IECPoint read GetQ;
+
+    function Equals(const other: IECPublicKeyParameters): Boolean; reintroduce; overload;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+  end;
+
+implementation
+
+{ TECPublicKeyParameters }
+
+function TECPublicKeyParameters.GetQ: IECPoint;
+begin
+  result := Fq;
+end;
+
+constructor TECPublicKeyParameters.Create(const algorithm: String;
+  const q: IECPoint; const parameters: IECDomainParameters);
+begin
+  Inherited Create(algorithm, false, parameters);
+
+  if (q = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SQNil);
+  end;
+
+  Fq := TECDomainParameters.Validate(parameters.Curve, q);
+end;
+
+constructor TECPublicKeyParameters.Create(const q: IECPoint;
+  const parameters: IECDomainParameters);
+begin
+  Create('EC', q, parameters);
+end;
+
+constructor TECPublicKeyParameters.Create(const algorithm: String;
+  const q: IECPoint; const publicKeyParamSet: IDerObjectIdentifier);
+begin
+  Inherited Create(algorithm, false, publicKeyParamSet);
+
+  if (q = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SQNil);
+  end;
+
+  Fq := TECDomainParameters.Validate(parameters.Curve, q);
+end;
+
+function TECPublicKeyParameters.Equals(const other
+  : IECPublicKeyParameters): Boolean;
+begin
+  if (other = Self as IECPublicKeyParameters) then
+  begin
+    result := true;
+    Exit;
+  end;
+
+  if (other = Nil) then
+  begin
+    result := false;
+    Exit;
+  end;
+  result := q.Equals(other.q) and (inherited Equals(other));
+end;
+
+function TECPublicKeyParameters.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := q.GetHashCode() xor (inherited GetHashCode());
+end;
+
+end.

+ 292 - 0
src/libraries/cryptolib4pascal/ClpECSchnorrSipaSigner.pas

@@ -0,0 +1,292 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpECSchnorrSipaSigner;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpIDigest,
+  ClpISchnorrExt,
+  ClpIECC,
+  ClpIECSchnorrSipaSigner,
+  ClpBigInteger,
+  ClpBigIntegers,
+  ClpISecureRandom,
+  ClpIECKeyParameters,
+  ClpIParametersWithRandom,
+  ClpICipherParameters,
+  ClpIECPrivateKeyParameters,
+  ClpIECPublicKeyParameters,
+  ClpSecureRandom,
+  ClpECAlgorithms,
+  ClpDigestUtilities,
+  ClpArrayUtils,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SECPublicKeyNotFound = 'EC Public Key Required for Verification';
+  SECPrivateKeyNotFound = 'EC Private Key Required for Signing';
+  SNotInitializedForSigning = 'Not Initialised For Signing';
+  SNotInitializedForVerifying = 'Not Initialised For Verifying';
+  SSignatureGenerationError = 'An Error Occurred During Signature Generation';
+  SOnlyFPCurvesAllowed =
+    'Only FP (Prime Field) Curves are Allowed for This Schnorr Implementation';
+
+type
+
+  /// <summary>
+  /// <para>
+  /// Schnorr Signature as described in <see href="https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki">
+  /// bip-schnorr</see>
+  /// </para>
+  /// <para>
+  /// This <c>Schnorr</c> implementation only allows <c>FP(Prime Field)</c>
+  /// Curves.
+  /// </para>
+  /// </summary>
+  TECSchnorrSipaSigner = class sealed(TInterfacedObject, ISchnorrExt,
+    IECSchnorrSipaSigner)
+
+  strict private
+  var
+    FForSigning: Boolean;
+    FKey: IECKeyParameters;
+    FRandom: ISecureRandom;
+    FDigest: IDigest;
+
+    function GetAlgorithmName: String; virtual;
+    function GetOrder: TBigInteger; virtual;
+
+    function GetPP: TBigInteger; inline;
+    function GetG: IECPoint; inline;
+    function GetCurve: IECCurve; inline;
+
+    property PP: TBigInteger read GetPP;
+    property G: IECPoint read GetG;
+    property Curve: IECCurve read GetCurve;
+
+    class procedure ValidateAllowedCurves(const ACurve: IECCurve);
+      static; inline;
+
+    procedure Reset();
+
+  public
+
+    property Order: TBigInteger read GetOrder;
+    property AlgorithmName: String read GetAlgorithmName;
+
+    procedure Init(forSigning: Boolean; const parameters: ICipherParameters;
+      const digest: IDigest); virtual;
+
+    function GenerateSignature(const &message: TCryptoLibByteArray)
+      : TCryptoLibGenericArray<TBigInteger>; virtual;
+
+    function VerifySignature(const &message: TCryptoLibByteArray;
+      const RSig, SSig: TBigInteger): Boolean; virtual;
+
+  end;
+
+implementation
+
+{ TECSchnorrSipaSigner }
+
+function TECSchnorrSipaSigner.GenerateSignature(const &message
+  : TCryptoLibByteArray): TCryptoLibGenericArray<TBigInteger>;
+var
+  N, k, s, Xr, Yr, e, PrivateKey: TBigInteger;
+  input, keyPrefixedM: TCryptoLibByteArray;
+  P, r: IECPoint;
+  numBytes: Int32;
+begin
+  if (not FForSigning) then
+  begin
+    // not properly initialized... deal with it
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SNotInitializedForSigning);
+  end;
+
+  N := Order;
+  numBytes := TBigIntegers.GetUnsignedByteLength(N);
+
+  PrivateKey := (FKey as IECPrivateKeyParameters).D;
+
+  input := TArrayUtils.Concatenate(TBigIntegers.BigIntegerToBytes(PrivateKey,
+    numBytes), &message);
+
+  k := TBigInteger.Create(1, TDigestUtilities.DoFinal(FDigest, input)).&Mod(N);
+
+  if k.CompareTo(TBigInteger.Zero) = 0 then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SSignatureGenerationError);
+  end;
+
+  r := G.Multiply(k).Normalize();
+  Xr := r.XCoord.ToBigInteger();
+  Yr := r.YCoord.ToBigInteger();
+  if (TBigInteger.Jacobi(Yr, PP) <> 1) then
+  begin
+    k := N.Subtract(k);
+  end;
+
+  P := G.Multiply(PrivateKey);
+  keyPrefixedM := TArrayUtils.Concatenate(TBigIntegers.BigIntegerToBytes(Xr,
+    numBytes), TCryptoLibMatrixByteArray.Create(P.GetEncoded(true), &message));
+
+  e := TBigInteger.Create(1, TDigestUtilities.DoFinal(FDigest,
+    keyPrefixedM)).&Mod(N);
+
+  s := k.Add(e.Multiply(PrivateKey)).&Mod(N);
+
+  result := TCryptoLibGenericArray<TBigInteger>.Create(Xr, s);
+end;
+
+function TECSchnorrSipaSigner.GetAlgorithmName: String;
+begin
+  result := 'ECSCHNORRSIPA';
+end;
+
+function TECSchnorrSipaSigner.GetCurve: IECCurve;
+begin
+  result := FKey.parameters.Curve;
+end;
+
+function TECSchnorrSipaSigner.GetG: IECPoint;
+begin
+  result := FKey.parameters.G;
+end;
+
+function TECSchnorrSipaSigner.GetOrder: TBigInteger;
+begin
+  result := FKey.parameters.N;
+end;
+
+function TECSchnorrSipaSigner.GetPP: TBigInteger;
+begin
+  result := Curve.Field.Characteristic;
+end;
+
+class procedure TECSchnorrSipaSigner.ValidateAllowedCurves
+  (const ACurve: IECCurve);
+begin
+  if (not(TECAlgorithms.IsFpCurve(ACurve))) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SOnlyFPCurvesAllowed);
+  end;
+end;
+
+procedure TECSchnorrSipaSigner.Init(forSigning: Boolean;
+  const parameters: ICipherParameters; const digest: IDigest);
+var
+  rParam: IParametersWithRandom;
+  Lparameters: ICipherParameters;
+begin
+  FForSigning := forSigning;
+  FDigest := digest;
+  Lparameters := parameters;
+
+  if (forSigning) then
+  begin
+
+    if (Supports(Lparameters, IParametersWithRandom, rParam)) then
+    begin
+      FRandom := rParam.random;
+      Lparameters := rParam.parameters;
+    end
+    else
+    begin
+      FRandom := TSecureRandom.Create();
+    end;
+
+    if (not(Supports(Lparameters, IECPrivateKeyParameters))) then
+    begin
+      raise EInvalidKeyCryptoLibException.CreateRes(@SECPrivateKeyNotFound);
+    end;
+
+    FKey := Lparameters as IECPrivateKeyParameters;
+  end
+  else
+  begin
+    if (not(Supports(Lparameters, IECPublicKeyParameters))) then
+    begin
+      raise EInvalidKeyCryptoLibException.CreateRes(@SECPublicKeyNotFound);
+    end;
+
+    FKey := Lparameters as IECPublicKeyParameters;
+  end;
+
+  ValidateAllowedCurves(Curve);
+  Reset();
+end;
+
+procedure TECSchnorrSipaSigner.Reset;
+begin
+  FDigest.Reset;
+end;
+
+function TECSchnorrSipaSigner.VerifySignature(const &message
+  : TCryptoLibByteArray; const RSig, SSig: TBigInteger): Boolean;
+var
+  N, e: TBigInteger;
+  PublicKeyBytes, input: TCryptoLibByteArray;
+  PublicKey: IECPublicKeyParameters;
+  P, Q, r: IECPoint;
+  numBytes: Int32;
+begin
+  if (FForSigning) then
+  begin
+    // not properly initialized... deal with it
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SNotInitializedForVerifying);
+  end;
+
+  N := Order;
+  numBytes := TBigIntegers.GetUnsignedByteLength(N);
+
+  if ((RSig.CompareTo(PP) >= 0) or (SSig.CompareTo(N) >= 0)) then
+  begin
+    result := false;
+    Exit;
+  end;
+
+  PublicKey := (FKey as IECPublicKeyParameters);
+  PublicKeyBytes := PublicKey.Q.GetEncoded(true);
+
+  input := TArrayUtils.Concatenate(TBigIntegers.BigIntegerToBytes(RSig,
+    numBytes), TCryptoLibMatrixByteArray.Create(PublicKeyBytes, &message));
+
+  e := TBigInteger.Create(1, TDigestUtilities.DoFinal(FDigest, input)).&Mod(N);
+  Q := PublicKey.Q.Normalize();
+  P := Curve.CreatePoint(Q.XCoord.ToBigInteger(), Q.YCoord.ToBigInteger());
+
+  r := G.Multiply(SSig).Add(P.Multiply(N.Subtract(e))).Normalize();
+
+  if ((r.IsInfinity) or (r.XCoord.ToBigInteger().CompareTo(RSig) <> 0) or
+    (TBigInteger.Jacobi(r.YCoord.ToBigInteger(), PP) <> 1)) then
+  begin
+    result := false;
+    Exit;
+  end;
+
+  result := true;
+end;
+
+end.

+ 147 - 0
src/libraries/cryptolib4pascal/ClpEacObjectIdentifiers.pas

@@ -0,0 +1,147 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpEacObjectIdentifiers;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpAsn1Objects,
+  ClpIAsn1Objects;
+
+type
+  TEacObjectIdentifiers = class abstract(TObject)
+
+  strict private
+
+  class var
+
+    FIsBooted: Boolean;
+    Fbsi_de, Fid_TA, Fid_TA_ECDSA, Fid_TA_ECDSA_SHA_1, Fid_TA_ECDSA_SHA_224,
+      Fid_TA_ECDSA_SHA_256, Fid_TA_ECDSA_SHA_384, Fid_TA_ECDSA_SHA_512
+      : IDerObjectIdentifier;
+
+    class function Getbsi_de: IDerObjectIdentifier; static; inline;
+    class function Getid_TA: IDerObjectIdentifier; static; inline;
+    class function Getid_TA_ECDSA: IDerObjectIdentifier; static; inline;
+    class function Getid_TA_ECDSA_SHA_1: IDerObjectIdentifier; static; inline;
+    class function Getid_TA_ECDSA_SHA_224: IDerObjectIdentifier; static; inline;
+    class function Getid_TA_ECDSA_SHA_256: IDerObjectIdentifier; static; inline;
+    class function Getid_TA_ECDSA_SHA_384: IDerObjectIdentifier; static; inline;
+    class function Getid_TA_ECDSA_SHA_512: IDerObjectIdentifier; static; inline;
+
+    class constructor EacObjectIdentifiers();
+
+  public
+    // bsi-de OBJECT IDENTIFIER ::= {
+    // itu-t(0) identified-organization(4) etsi(0)
+    // reserved(127) etsi-identified-organization(0) 7
+    // }
+    class property bsi_de: IDerObjectIdentifier read Getbsi_de;
+    //
+    // id-TA OBJECT IDENTIFIER ::= {
+    // bsi-de protocols(2) smartcard(2) 2
+    // }
+    class property id_TA: IDerObjectIdentifier read Getid_TA;
+    class property id_TA_ECDSA: IDerObjectIdentifier read Getid_TA_ECDSA;
+    class property id_TA_ECDSA_SHA_1: IDerObjectIdentifier
+      read Getid_TA_ECDSA_SHA_1;
+    class property id_TA_ECDSA_SHA_224: IDerObjectIdentifier
+      read Getid_TA_ECDSA_SHA_224;
+    class property id_TA_ECDSA_SHA_256: IDerObjectIdentifier
+      read Getid_TA_ECDSA_SHA_256;
+    class property id_TA_ECDSA_SHA_384: IDerObjectIdentifier
+      read Getid_TA_ECDSA_SHA_384;
+    class property id_TA_ECDSA_SHA_512: IDerObjectIdentifier
+      read Getid_TA_ECDSA_SHA_512;
+    class procedure Boot(); static;
+
+  end;
+
+implementation
+
+{ TEacObjectIdentifiers }
+
+class procedure TEacObjectIdentifiers.Boot;
+begin
+  if not FIsBooted then
+  begin
+    Fbsi_de := TDerObjectIdentifier.Create('0.4.0.127.0.7');
+    Fid_TA := TDerObjectIdentifier.Create(Fbsi_de.ID + '.2.2.2');
+    Fid_TA_ECDSA := TDerObjectIdentifier.Create(Fid_TA.ID + '.2');
+    Fid_TA_ECDSA_SHA_1 := TDerObjectIdentifier.Create(Fid_TA_ECDSA.ID + '.1');
+    Fid_TA_ECDSA_SHA_224 := TDerObjectIdentifier.Create(Fid_TA_ECDSA.ID + '.2');
+    Fid_TA_ECDSA_SHA_256 := TDerObjectIdentifier.Create(Fid_TA_ECDSA.ID + '.3');
+    Fid_TA_ECDSA_SHA_384 := TDerObjectIdentifier.Create(Fid_TA_ECDSA.ID + '.4');
+    Fid_TA_ECDSA_SHA_512 := TDerObjectIdentifier.Create(Fid_TA_ECDSA.ID + '.5');
+
+    FIsBooted := True;
+  end;
+end;
+
+class constructor TEacObjectIdentifiers.EacObjectIdentifiers;
+begin
+  TEacObjectIdentifiers.Boot;
+end;
+
+class function TEacObjectIdentifiers.Getbsi_de: IDerObjectIdentifier;
+begin
+  result := Fbsi_de;
+end;
+
+class function TEacObjectIdentifiers.Getid_TA: IDerObjectIdentifier;
+begin
+  result := Fid_TA;
+end;
+
+class function TEacObjectIdentifiers.Getid_TA_ECDSA: IDerObjectIdentifier;
+begin
+  result := Fid_TA_ECDSA;
+end;
+
+class function TEacObjectIdentifiers.Getid_TA_ECDSA_SHA_1: IDerObjectIdentifier;
+begin
+  result := Fid_TA_ECDSA_SHA_1;
+end;
+
+class function TEacObjectIdentifiers.Getid_TA_ECDSA_SHA_224
+  : IDerObjectIdentifier;
+begin
+  result := Fid_TA_ECDSA_SHA_224;
+end;
+
+class function TEacObjectIdentifiers.Getid_TA_ECDSA_SHA_256
+  : IDerObjectIdentifier;
+begin
+  result := Fid_TA_ECDSA_SHA_256;
+end;
+
+class function TEacObjectIdentifiers.Getid_TA_ECDSA_SHA_384
+  : IDerObjectIdentifier;
+begin
+  result := Fid_TA_ECDSA_SHA_384;
+end;
+
+class function TEacObjectIdentifiers.Getid_TA_ECDSA_SHA_512
+  : IDerObjectIdentifier;
+begin
+  result := Fid_TA_ECDSA_SHA_512;
+end;
+
+end.

+ 103 - 0
src/libraries/cryptolib4pascal/ClpEncoders.pas

@@ -0,0 +1,103 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpEncoders;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SbpBase16,
+  SbpBase58,
+  SbpBase64,
+{$IFDEF DELPHI}
+  SbpIBase58,
+  SbpIBase64,
+{$ENDIF DELPHI}
+  ClpCryptoLibTypes;
+
+type
+  TBase58 = class sealed(TObject)
+
+  public
+    class function Encode(const Input: TCryptoLibByteArray): String; static;
+    class function Decode(const Input: String): TCryptoLibByteArray; static;
+  end;
+
+type
+  TBase64 = class sealed(TObject)
+
+  public
+    class function Encode(const Input: TCryptoLibByteArray): String; static;
+    class function Decode(const Input: String): TCryptoLibByteArray; static;
+  end;
+
+type
+  THex = class sealed(TObject)
+
+  public
+    class function Decode(const Hex: String): TCryptoLibByteArray; static;
+    class function Encode(const Input: TCryptoLibByteArray;
+      UpperCase: Boolean = True): String; static;
+  end;
+
+implementation
+
+{ TBase58 }
+
+class function TBase58.Decode(const Input: String): TCryptoLibByteArray;
+begin
+  result := SbpBase58.TBase58.BitCoin.Decode(Input);
+end;
+
+class function TBase58.Encode(const Input: TCryptoLibByteArray): String;
+begin
+  result := SbpBase58.TBase58.BitCoin.Encode(Input);
+end;
+
+{ TBase64 }
+
+class function TBase64.Decode(const Input: String): TCryptoLibByteArray;
+begin
+  result := SbpBase64.TBase64.Default.Decode(Input);
+end;
+
+class function TBase64.Encode(const Input: TCryptoLibByteArray): String;
+begin
+  result := SbpBase64.TBase64.Default.Encode(Input);
+end;
+
+{ THex }
+
+class function THex.Decode(const Hex: String): TCryptoLibByteArray;
+begin
+  result := SbpBase16.TBase16.Decode(Hex);
+end;
+
+class function THex.Encode(const Input: TCryptoLibByteArray;
+  UpperCase: Boolean): String;
+begin
+  case UpperCase of
+    True:
+      result := SbpBase16.TBase16.EncodeUpper(Input);
+    False:
+      result := SbpBase16.TBase16.EncodeLower(Input);
+  end;
+end;
+
+end.

+ 71 - 0
src/libraries/cryptolib4pascal/ClpEphemeralKeyPair.pas

@@ -0,0 +1,71 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpEphemeralKeyPair;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIEphemeralKeyPair,
+  ClpIAsymmetricCipherKeyPair,
+  ClpIKeyEncoder,
+  ClpCryptoLibTypes;
+
+type
+  TEphemeralKeyPair = class sealed(TInterfacedObject, IEphemeralKeyPair)
+
+  strict private
+  var
+    FkeyPair: IAsymmetricCipherKeyPair;
+    FpublicKeyEncoder: IKeyEncoder;
+
+  public
+
+    function GetKeyPair(): IAsymmetricCipherKeyPair; inline;
+
+    function GetEncodedPublicKey: TCryptoLibByteArray; inline;
+
+    constructor Create(const keyPair: IAsymmetricCipherKeyPair;
+      const publicKeyEncoder: IKeyEncoder);
+
+  end;
+
+implementation
+
+{ TEphemeralKeyPair }
+
+constructor TEphemeralKeyPair.Create(const keyPair: IAsymmetricCipherKeyPair;
+  const publicKeyEncoder: IKeyEncoder);
+begin
+  Inherited Create();
+  FkeyPair := keyPair;
+  FpublicKeyEncoder := publicKeyEncoder;
+end;
+
+function TEphemeralKeyPair.GetEncodedPublicKey: TCryptoLibByteArray;
+begin
+  result := FpublicKeyEncoder.GetEncoded(FkeyPair.Public);
+end;
+
+function TEphemeralKeyPair.GetKeyPair: IAsymmetricCipherKeyPair;
+begin
+  result := FkeyPair;
+end;
+
+end.

+ 68 - 0
src/libraries/cryptolib4pascal/ClpEphemeralKeyPairGenerator.pas

@@ -0,0 +1,68 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpEphemeralKeyPairGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpEphemeralKeyPair,
+  ClpIEphemeralKeyPair,
+  ClpIEphemeralKeyPairGenerator,
+  ClpIAsymmetricCipherKeyPair,
+  ClpIAsymmetricCipherKeyPairGenerator,
+  ClpIKeyEncoder;
+
+type
+  TEphemeralKeyPairGenerator = class sealed(TInterfacedObject,
+    IEphemeralKeyPairGenerator)
+
+  strict private
+  var
+    Fgen: IAsymmetricCipherKeyPairGenerator;
+    FkeyEncoder: IKeyEncoder;
+
+  public
+    function Generate(): IEphemeralKeyPair; inline;
+    constructor Create(const gen: IAsymmetricCipherKeyPairGenerator;
+      const keyEncoder: IKeyEncoder);
+  end;
+
+implementation
+
+{ TEphemeralKeyPairGenerator }
+
+constructor TEphemeralKeyPairGenerator.Create
+  (const gen: IAsymmetricCipherKeyPairGenerator; const keyEncoder: IKeyEncoder);
+begin
+  Inherited Create();
+  Fgen := gen;
+  FkeyEncoder := keyEncoder;
+end;
+
+function TEphemeralKeyPairGenerator.Generate: IEphemeralKeyPair;
+var
+  eph: IAsymmetricCipherKeyPair;
+begin
+  eph := Fgen.generateKeyPair();
+  // Encode the ephemeral public key
+  result := TEphemeralKeyPair.Create(eph, FkeyEncoder);
+end;
+
+end.

+ 132 - 0
src/libraries/cryptolib4pascal/ClpFiniteFields.pas

@@ -0,0 +1,132 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpFiniteFields;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpBigInteger,
+  ClpCryptoLibTypes,
+  ClpPrimeField,
+  ClpGF2Polynomial,
+  ClpIGF2Polynomial,
+  ClpGenericPolynomialExtensionField,
+  ClpIPolynomialExtensionField,
+  ClpIFiniteField;
+
+resourcestring
+  SInvalidCharacteristic = 'Must be >= 2 , " characteristic "';
+  SUnConstantTerm =
+    'Irreducible polynomials in GF(2) must have constant term, "exponents"';
+  SPolynomialError =
+    'Polynomial Exponents must be montonically increasing", "exponents"';
+
+type
+  TFiniteFields = class abstract(TObject)
+
+  strict private
+    class var
+
+      FGF_2, FGF_3: IFiniteField;
+
+    class procedure Boot(); static;
+    class constructor FiniteFields();
+
+  public
+    class function GetBinaryExtensionField(const exponents
+      : TCryptoLibInt32Array): IPolynomialExtensionField; static;
+
+    class function GetPrimeField(const characteristic: TBigInteger)
+      : IFiniteField; static;
+  end;
+
+implementation
+
+{ TFiniteFields }
+
+class procedure TFiniteFields.Boot;
+begin
+
+  FGF_2 := TPrimeField.Create(TBigInteger.ValueOf(2));
+  FGF_3 := TPrimeField.Create(TBigInteger.ValueOf(3));
+end;
+
+class constructor TFiniteFields.FiniteFields;
+begin
+  TFiniteFields.Boot;
+end;
+
+class function TFiniteFields.GetBinaryExtensionField(const exponents
+  : TCryptoLibInt32Array): IPolynomialExtensionField;
+var
+  i: Int32;
+begin
+  if (exponents[0] <> 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SUnConstantTerm);
+  end;
+
+  for i := 1 to System.Pred(System.Length(exponents)) do
+
+  begin
+    if (exponents[i] <= exponents[i - 1]) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes(@SPolynomialError);
+    end;
+  end;
+
+  Result := TGenericPolynomialExtensionField.Create(FGF_2,
+    TGF2Polynomial.Create(exponents) as IGF2Polynomial);
+end;
+
+class function TFiniteFields.GetPrimeField(const characteristic: TBigInteger)
+  : IFiniteField;
+var
+  bitLength: Int32;
+begin
+  bitLength := characteristic.bitLength;
+  if ((characteristic.SignValue <= 0) or (bitLength < 2)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidCharacteristic);
+  end;
+
+  if (bitLength < 3) then
+  begin
+    case characteristic.Int32Value of
+      2:
+        begin
+          Result := FGF_2;
+          Exit;
+        end;
+
+      3:
+        begin
+          Result := FGF_3;
+          Exit;
+        end;
+    end;
+
+  end;
+
+  Result := TPrimeField.Create(characteristic);
+
+end;
+
+end.

+ 130 - 0
src/libraries/cryptolib4pascal/ClpFixedPointCombMultiplier.pas

@@ -0,0 +1,130 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpFixedPointCombMultiplier;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpBits,
+  ClpBigInteger,
+  ClpNat,
+  ClpCryptoLibTypes,
+  ClpIECC,
+  ClpFixedPointUtilities,
+  ClpIFixedPointPreCompInfo,
+  ClpAbstractECMultiplier,
+  ClpIFixedPointCombMultiplier;
+
+resourcestring
+  SInvalidComputation =
+    'Fixed-Point Comb Doesn''t Support Scalars Larger Than The Curve Order';
+
+type
+  TFixedPointCombMultiplier = class sealed(TAbstractECMultiplier,
+    IFixedPointCombMultiplier)
+
+  strict protected
+    function MultiplyPositive(const p: IECPoint; const k: TBigInteger)
+      : IECPoint; override;
+
+  public
+    constructor Create();
+    destructor Destroy; override;
+
+  end;
+
+implementation
+
+{ TFixedPointCombMultiplier }
+
+constructor TFixedPointCombMultiplier.Create;
+begin
+  Inherited Create();
+end;
+
+destructor TFixedPointCombMultiplier.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TFixedPointCombMultiplier.MultiplyPositive(const p: IECPoint;
+  const k: TBigInteger): IECPoint;
+var
+  c: IECCurve;
+  R, add: IECPoint;
+  size, width, d, top, i, j, fullComb: Int32;
+  secretIndex, secretBit: UInt32;
+  info: IFixedPointPreCompInfo;
+  lookupTable: IECLookupTable;
+  LK: TCryptoLibUInt32Array;
+begin
+  c := p.Curve;
+  size := TFixedPointUtilities.GetCombSize(c);
+  if (k.BitLength > size) then
+  begin
+    // /*
+    // * TODO The comb works best when the scalars are less than the (possibly unknown) order.
+    // * Still, if we want to handle larger scalars, we could allow customization of the comb
+    // * size, or alternatively we could deal with the 'extra' bits either by running the comb
+    // * multiple times as necessary, or by using an alternative multiplier as prelude.
+    // */
+    raise EInvalidOperationCryptoLibException.CreateRes(@SInvalidComputation);
+  end;
+
+  info := TFixedPointUtilities.Precompute(p);
+  lookupTable := info.lookupTable;
+  width := info.width;
+
+  d := (size + width - 1) div width;
+
+  R := c.Infinity;
+  fullComb := d * width;
+  LK := TNat.FromBigInteger(fullComb, k);
+
+  top := fullComb - 1;
+
+  for i := 0 to System.Pred(d) do
+  begin
+
+    secretIndex := 0;
+
+    j := (top - i);
+
+    while j >= 0 do
+    begin
+
+      secretBit := LK[TBits.Asr32(j, 5)] shr (j and $1F);
+      secretIndex := secretIndex xor (secretBit shr 1);
+      secretIndex := secretIndex shl 1;
+      secretIndex := secretIndex xor secretBit;
+
+      System.Dec(j, d);
+    end;
+
+    add := lookupTable.Lookup(Int32(secretIndex));
+    R := R.TwicePlus(add);
+
+  end;
+
+  Result := R.add(info.Offset);
+
+end;
+
+end.

+ 114 - 0
src/libraries/cryptolib4pascal/ClpFixedPointPreCompInfo.pas

@@ -0,0 +1,114 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpFixedPointPreCompInfo;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpCryptoLibTypes,
+  ClpIECC,
+  ClpIFixedPointPreCompInfo,
+  ClpIPreCompInfo;
+
+type
+
+  /// <summary>
+  /// Class holding precomputation data for fixed-point multiplications.
+  /// </summary>
+  TFixedPointPreCompInfo = class(TInterfacedObject, IPreCompInfo,
+    IFixedPointPreCompInfo)
+
+  strict private
+    function GetWidth: Int32;
+    procedure SetWidth(const Value: Int32);
+
+    function GetLookupTable: IECLookupTable;
+    procedure SetLookupTable(const Value: IECLookupTable);
+
+    function GetOffset: IECPoint;
+    procedure SetOffset(const Value: IECPoint);
+
+  strict protected
+  var
+    Fm_offset: IECPoint;
+
+    /// <summary>
+    /// Array holding the precomputed <c>ECPoint</c>s used for a fixed point
+    /// multiplication.
+    /// </summary>
+    Fm_lookupTable: IECLookupTable;
+
+    /// <summary>
+    /// The width used for the precomputation. If a larger width
+    /// precomputation is already available this may be larger than was
+    /// requested, so calling code should refer to the actual width.
+    /// </summary>
+    Fm_width: Int32;
+
+  public
+    constructor Create();
+    property Offset: IECPoint read GetOffset write SetOffset;
+    property LookupTable: IECLookupTable read GetLookupTable
+      write SetLookupTable;
+    property Width: Int32 read GetWidth write SetWidth;
+
+  end;
+
+implementation
+
+{ TFixedPointPreCompInfo }
+
+constructor TFixedPointPreCompInfo.Create;
+begin
+  inherited Create();
+  Fm_width := -1;
+end;
+
+function TFixedPointPreCompInfo.GetLookupTable: IECLookupTable;
+begin
+  Result := Fm_lookupTable;
+end;
+
+function TFixedPointPreCompInfo.GetOffset: IECPoint;
+begin
+  Result := Fm_offset;
+end;
+
+function TFixedPointPreCompInfo.GetWidth: Int32;
+begin
+  Result := Fm_width;
+end;
+
+procedure TFixedPointPreCompInfo.SetLookupTable(const Value: IECLookupTable);
+begin
+  Fm_lookupTable := Value;
+end;
+
+procedure TFixedPointPreCompInfo.SetOffset(const Value: IECPoint);
+begin
+  Fm_offset := Value;
+end;
+
+procedure TFixedPointPreCompInfo.SetWidth(const Value: Int32);
+begin
+  Fm_width := Value;
+end;
+
+end.

+ 219 - 0
src/libraries/cryptolib4pascal/ClpFixedPointUtilities.pas

@@ -0,0 +1,219 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpFixedPointUtilities;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpBigInteger,
+  ClpCryptoLibTypes,
+  ClpIPreCompInfo,
+  ClpIPreCompCallback,
+  ClpFixedPointPreCompInfo,
+  ClpIFixedPointPreCompInfo,
+  ClpIECC;
+
+type
+  TFixedPointUtilities = class sealed(TObject)
+  strict private
+
+  type
+    IFixedPointCallback = interface(IPreCompCallback)
+      ['{E6DFE8D3-A890-4568-AA4A-3D8BC6AF16E9}']
+
+    end;
+
+  type
+    TFixedPointCallback = class(TInterfacedObject, IPreCompCallback,
+      IFixedPointCallback)
+
+    strict private
+    var
+      Fm_p: IECPoint;
+
+    public
+      constructor Create(const p: IECPoint);
+
+      function Precompute(const existing: IPreCompInfo): IPreCompInfo;
+
+    end;
+
+  const
+    PRECOMP_NAME: String = 'bc_fixed_point';
+
+    class function CheckExisting(const existingFP: IFixedPointPreCompInfo;
+      n: Int32): Boolean; static; inline;
+
+    class function CheckTable(const table: IECLookupTable; n: Int32): Boolean;
+      static; inline;
+
+  public
+
+    class function GetFixedPointPreCompInfo(const preCompInfo: IPreCompInfo)
+      : IFixedPointPreCompInfo; static; inline;
+
+    class function GetCombSize(const c: IECCurve): Int32; static; inline;
+
+    class function Precompute(const p: IECPoint)
+      : IFixedPointPreCompInfo; static;
+  end;
+
+implementation
+
+{ TFixedPointUtilities }
+
+class function TFixedPointUtilities.CheckTable(const table: IECLookupTable;
+  n: Int32): Boolean;
+begin
+  result := (table <> Nil) and (table.Size >= n);
+end;
+
+class function TFixedPointUtilities.CheckExisting(const existingFP
+  : IFixedPointPreCompInfo; n: Int32): Boolean;
+begin
+  result := (existingFP <> Nil) and CheckTable(existingFP.LookUpTable, n);
+end;
+
+class function TFixedPointUtilities.GetCombSize(const c: IECCurve): Int32;
+var
+  order: TBigInteger;
+begin
+  order := c.order;
+  if (not(order.IsInitialized)) then
+  begin
+    result := c.FieldSize + 1;
+  end
+  else
+  begin
+    result := order.BitLength;
+  end;
+end;
+
+class function TFixedPointUtilities.GetFixedPointPreCompInfo(const preCompInfo
+  : IPreCompInfo): IFixedPointPreCompInfo;
+begin
+  result := preCompInfo as IFixedPointPreCompInfo;
+end;
+
+class function TFixedPointUtilities.Precompute(const p: IECPoint)
+  : IFixedPointPreCompInfo;
+var
+  c: IECCurve;
+begin
+  c := p.Curve;
+
+  result := c.Precompute(p, PRECOMP_NAME, TFixedPointCallback.Create(p)
+    as IFixedPointCallback) as IFixedPointPreCompInfo;
+end;
+
+{ TFixedPointUtilities.TFixedPointCallback }
+
+constructor TFixedPointUtilities.TFixedPointCallback.Create(const p: IECPoint);
+begin
+  Inherited Create();
+  Fm_p := p;
+end;
+
+function TFixedPointUtilities.TFixedPointCallback.Precompute(const existing
+  : IPreCompInfo): IPreCompInfo;
+var
+  bit, bits, minWidth, n, d, i, step: Int32;
+  existingFP: IFixedPointPreCompInfo;
+  pow2Table, LookUpTable: TCryptoLibGenericArray<IECPoint>;
+  pow2: IECPoint;
+  c: IECCurve;
+  tempResult: IFixedPointPreCompInfo;
+begin
+  if Supports(existing, IFixedPointPreCompInfo) then
+  begin
+    existingFP := existing as IFixedPointPreCompInfo;
+  end
+  else
+  begin
+    existingFP := Nil;
+  end;
+
+  c := Fm_p.Curve;
+  bits := TFixedPointUtilities.GetCombSize(c);
+  if bits > 250 then
+  begin
+    minWidth := 6
+  end
+  else
+  begin
+    minWidth := 5
+  end;
+  n := 1 shl minWidth;
+
+  if (CheckExisting(existingFP, n)) then
+  begin
+    result := existingFP;
+    Exit;
+  end;
+
+  d := (bits + minWidth - 1) div minWidth;
+
+  System.SetLength(pow2Table, minWidth + 1);
+
+  pow2Table[0] := Fm_p;
+  for i := 1 to System.Pred(minWidth) do
+  begin
+    pow2Table[i] := pow2Table[i - 1].TimesPow2(d);
+  end;
+
+  // This will be the 'offset' value
+  pow2Table[minWidth] := pow2Table[0].Subtract(pow2Table[1]);
+
+  c.NormalizeAll(pow2Table);
+
+  System.SetLength(LookUpTable, n);
+  LookUpTable[0] := pow2Table[0];
+
+  bit := minWidth - 1;
+  while bit >= 0 do
+  begin
+    pow2 := pow2Table[bit];
+
+    step := 1 shl bit;
+
+    i := step;
+
+    while i < n do
+    begin
+      LookUpTable[i] := LookUpTable[i - step].Add(pow2);
+
+      System.Inc(i, step shl 1);
+    end;
+
+    System.Dec(bit);
+  end;
+
+  c.NormalizeAll(LookUpTable);
+
+  tempResult := TFixedPointPreCompInfo.Create();
+  tempResult.LookUpTable := c.CreateCacheSafeLookupTable(LookUpTable, 0,
+    System.length(LookUpTable));
+  tempResult.Offset := pow2Table[minWidth];
+  tempResult.Width := minWidth;
+  result := tempResult;
+end;
+
+end.

+ 109 - 0
src/libraries/cryptolib4pascal/ClpGF2Polynomial.pas

@@ -0,0 +1,109 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpGF2Polynomial;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpCryptoLibTypes,
+  ClpArrayUtils,
+  ClpIGF2Polynomial,
+  ClpIPolynomial;
+
+type
+  TGF2Polynomial = class(TInterfacedObject, IPolynomial, IGF2Polynomial)
+
+  strict private
+    function GetDegree: Int32; virtual;
+    function GetExponents: TCryptoLibInt32Array; inline;
+  strict protected
+  var
+    Fexponents: TCryptoLibInt32Array;
+
+  public
+    constructor Create(const exponents: TCryptoLibInt32Array);
+
+    function GetExponentsPresent(): TCryptoLibInt32Array; virtual;
+
+    function Equals(other: TObject): Boolean; overload; override;
+    function Equals(const other: IGF2Polynomial): Boolean; reintroduce;
+      overload;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    property Degree: Int32 read GetDegree;
+
+    property exponents: TCryptoLibInt32Array read GetExponents;
+
+  end;
+
+implementation
+
+{ TGF2Polynomial }
+
+constructor TGF2Polynomial.Create(const exponents: TCryptoLibInt32Array);
+begin
+  Fexponents := System.Copy(exponents);
+end;
+
+function TGF2Polynomial.Equals(const other: IGF2Polynomial): Boolean;
+begin
+
+  if ((Self as IGF2Polynomial) = other) then
+  begin
+    Result := true;
+    Exit;
+  end;
+
+  if (other = Nil) then
+  begin
+    Result := false;
+    Exit;
+  end;
+  Result := TArrayUtils.AreEqual(Fexponents, other.exponents);
+end;
+
+function TGF2Polynomial.Equals(other: TObject): Boolean;
+begin
+  Result := Self.Equals((other as TGF2Polynomial) as IGF2Polynomial);
+end;
+
+function TGF2Polynomial.GetDegree: Int32;
+begin
+  Result := Fexponents[System.Length(Fexponents) - 1];
+end;
+
+function TGF2Polynomial.GetExponents: TCryptoLibInt32Array;
+begin
+  Result := Fexponents;
+end;
+
+function TGF2Polynomial.GetExponentsPresent: TCryptoLibInt32Array;
+begin
+  Result := System.Copy(Fexponents);
+end;
+
+function TGF2Polynomial.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  Result := TArrayUtils.GetArrayHashCode(Fexponents);
+end;
+
+end.

+ 387 - 0
src/libraries/cryptolib4pascal/ClpGeneratorUtilities.pas

@@ -0,0 +1,387 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpGeneratorUtilities;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Generics.Collections,
+  ClpECKeyPairGenerator,
+  ClpIECKeyPairGenerator,
+  ClpCipherKeyGenerator,
+  ClpICipherKeyGenerator,
+  ClpIAsn1Objects,
+  ClpDsaKeyPairGenerator,
+  ClpIDsaKeyPairGenerator,
+  ClpIAsymmetricCipherKeyPairGenerator,
+  ClpNistObjectIdentifiers,
+  ClpIanaObjectIdentifiers,
+  ClpPkcsObjectIdentifiers,
+  ClpRosstandartObjectIdentifiers,
+  ClpStringUtils,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SKeyGeneratorAlgorithmNotRecognised = 'KeyGenerator "%s" not Recognised.';
+  SKeyGeneratorAlgorithmNotSupported =
+    'KeyGenerator "%s" ( "%s" ) not Supported.';
+  SKeyPairGeneratorAlgorithmNotRecognised =
+    'KeyPairGenerator "%s" not Recognised.';
+  SKeyPairGeneratorAlgorithmNotSupported =
+    'KeyPairGenerator "%s" ( "%s" ) not Supported.';
+
+type
+
+  TGeneratorUtilities = class sealed(TObject)
+
+  strict private
+  class var
+
+    FkgAlgorithms: TDictionary<String, String>;
+    FkpgAlgorithms: TDictionary<String, String>;
+    FdefaultKeySizes: TDictionary<String, Int32>;
+
+    class function FindDefaultKeySize(const canonicalName: String): Int32;
+      static; inline;
+
+    class procedure AddDefaultKeySizeEntries(size: Int32;
+      const algorithms: array of String); static;
+
+    class procedure AddKgAlgorithm(const canonicalName: String;
+      const aliases: array of String); static;
+
+    class procedure AddKpgAlgorithm(const canonicalName: String;
+      const aliases: array of String); static;
+
+    class procedure AddHMacKeyGenerator(const algorithm: String;
+      const aliases: array of String); static;
+
+    class procedure Boot(); static;
+
+    class constructor CreateGeneratorUtilities();
+    class destructor DestroyGeneratorUtilities();
+
+  public
+
+    class function GetCanonicalKeyGeneratorAlgorithm(const algorithm: String)
+      : String; static; inline;
+
+    class function GetCanonicalKeyPairGeneratorAlgorithm(const algorithm
+      : String): String; static; inline;
+
+    class function GetKeyPairGenerator(const oid: IDerObjectIdentifier)
+      : IAsymmetricCipherKeyPairGenerator; overload; static; inline;
+
+    class function GetKeyPairGenerator(const algorithm: String)
+      : IAsymmetricCipherKeyPairGenerator; overload; static;
+
+    class function GetKeyGenerator(const algorithm: String)
+      : ICipherKeyGenerator; static;
+
+    class function GetDefaultKeySize(const oid: IDerObjectIdentifier): Int32;
+      overload; static; inline;
+
+    class function GetDefaultKeySize(const algorithm: String): Int32;
+      overload; static;
+
+  end;
+
+implementation
+
+{ TGeneratorUtilities }
+
+class procedure TGeneratorUtilities.AddDefaultKeySizeEntries(size: Int32;
+  const algorithms: array of String);
+var
+  algorithm: string;
+begin
+  for algorithm in algorithms do
+  begin
+    FdefaultKeySizes.Add(algorithm, size);
+  end;
+
+end;
+
+class procedure TGeneratorUtilities.AddKgAlgorithm(const canonicalName: String;
+  const aliases: array of String);
+var
+  alias: string;
+begin
+  FkgAlgorithms.Add(canonicalName, canonicalName);
+  for alias in aliases do
+  begin
+    FkgAlgorithms.Add(alias, canonicalName);
+  end;
+
+end;
+
+class procedure TGeneratorUtilities.AddKpgAlgorithm(const canonicalName: String;
+  const aliases: array of String);
+var
+  alias: string;
+begin
+  FkpgAlgorithms.Add(canonicalName, canonicalName);
+  for alias in aliases do
+  begin
+    FkpgAlgorithms.Add(alias, canonicalName);
+  end;
+
+end;
+
+class procedure TGeneratorUtilities.AddHMacKeyGenerator(const algorithm: String;
+  const aliases: array of String);
+var
+  alias, mainName: string;
+begin
+  mainName := 'HMAC' + algorithm;
+
+  FkgAlgorithms.Add(mainName, mainName);
+  FkgAlgorithms.Add('HMAC-' + algorithm, mainName);
+  FkgAlgorithms.Add('HMAC/' + algorithm, mainName);
+
+  for alias in aliases do
+  begin
+    FkgAlgorithms.Add(alias, mainName);
+  end;
+
+end;
+
+class procedure TGeneratorUtilities.Boot;
+begin
+  FkgAlgorithms := TDictionary<String, String>.Create();
+  FkpgAlgorithms := TDictionary<String, String>.Create();
+  FdefaultKeySizes := TDictionary<String, Int32>.Create();
+
+  TNistObjectIdentifiers.Boot;
+
+  //
+  // key generators.
+  //
+
+  AddKgAlgorithm('AES128', ['2.16.840.1.101.3.4.2',
+    TNistObjectIdentifiers.IdAes128Cbc.ID,
+    TNistObjectIdentifiers.IdAes128Cfb.ID,
+    TNistObjectIdentifiers.IdAes128Ecb.ID,
+    TNistObjectIdentifiers.IdAes128Ofb.ID]);
+
+  AddKgAlgorithm('AES192', ['2.16.840.1.101.3.4.22',
+    TNistObjectIdentifiers.IdAes192Cbc.ID,
+    TNistObjectIdentifiers.IdAes192Cfb.ID,
+    TNistObjectIdentifiers.IdAes192Ecb.ID,
+    TNistObjectIdentifiers.IdAes192Ofb.ID]);
+
+  AddKgAlgorithm('AES256', ['2.16.840.1.101.3.4.42',
+    TNistObjectIdentifiers.IdAes256Cbc.ID,
+    TNistObjectIdentifiers.IdAes256Cfb.ID,
+    TNistObjectIdentifiers.IdAes256Ecb.ID,
+    TNistObjectIdentifiers.IdAes256Ofb.ID]);
+
+  AddKgAlgorithm('BLOWFISH', ['1.3.6.1.4.1.3029.1.2']);
+
+  //
+  // HMac key generators
+  //
+  TIanaObjectIdentifiers.Boot;
+
+  AddHMacKeyGenerator('MD2', []);
+  AddHMacKeyGenerator('MD4', []);
+  AddHMacKeyGenerator('MD5', [TIanaObjectIdentifiers.HmacMD5.ID]);
+
+  TPkcsObjectIdentifiers.Boot;
+
+  AddHMacKeyGenerator('SHA1', [TPkcsObjectIdentifiers.IdHmacWithSha1.ID,
+    TIanaObjectIdentifiers.HmacSha1.ID]);
+  AddHMacKeyGenerator('SHA224', [TPkcsObjectIdentifiers.IdHmacWithSha224.ID]);
+  AddHMacKeyGenerator('SHA256', [TPkcsObjectIdentifiers.IdHmacWithSha256.ID]);
+  AddHMacKeyGenerator('SHA384', [TPkcsObjectIdentifiers.IdHmacWithSha384.ID]);
+  AddHMacKeyGenerator('SHA512', [TPkcsObjectIdentifiers.IdHmacWithSha512.ID]);
+  AddHMacKeyGenerator('SHA512/224', []);
+  AddHMacKeyGenerator('SHA512/256', []);
+
+  AddHMacKeyGenerator('SHA3-224',
+    [TNistObjectIdentifiers.IdHMacWithSha3_224.ID]);
+  AddHMacKeyGenerator('SHA3-256',
+    [TNistObjectIdentifiers.IdHMacWithSha3_256.ID]);
+  AddHMacKeyGenerator('SHA3-384',
+    [TNistObjectIdentifiers.IdHMacWithSha3_384.ID]);
+  AddHMacKeyGenerator('SHA3-512',
+    [TNistObjectIdentifiers.IdHMacWithSha3_512.ID]);
+  AddHMacKeyGenerator('RIPEMD128', []);
+  AddHMacKeyGenerator('RIPEMD160', [TIanaObjectIdentifiers.HmacRipeMD160.ID]);
+  AddHMacKeyGenerator('TIGER', [TIanaObjectIdentifiers.HmacTiger.ID]);
+
+  TRosstandartObjectIdentifiers.Boot;
+
+  AddHMacKeyGenerator('GOST3411-2012-256',
+    [TRosstandartObjectIdentifiers.id_tc26_hmac_gost_3411_12_256.ID]);
+  AddHMacKeyGenerator('GOST3411-2012-512',
+    [TRosstandartObjectIdentifiers.id_tc26_hmac_gost_3411_12_512.ID]);
+
+  //
+  // key pair generators.
+  //
+
+  AddKpgAlgorithm('DSA', []);
+  AddKpgAlgorithm('ECDH', ['ECIES']);
+  AddKpgAlgorithm('ECDSA', []);
+
+  AddDefaultKeySizeEntries(128, ['AES128', 'BLOWFISH', 'HMACMD2', 'HMACMD4',
+    'HMACMD5', 'HMACRIPEMD128']);
+  AddDefaultKeySizeEntries(160, ['HMACRIPEMD160', 'HMACSHA1']);
+  AddDefaultKeySizeEntries(192, ['AES', 'AES192', 'HMACTIGER']);
+  AddDefaultKeySizeEntries(224, ['HMACSHA3-224', 'HMACSHA224',
+    'HMACSHA512/224']);
+  AddDefaultKeySizeEntries(256, ['AES256', 'HMACGOST3411-2012-256',
+    'HMACSHA3-256', 'HMACSHA256', 'HMACSHA512/256']);
+  AddDefaultKeySizeEntries(384, ['HMACSHA3-384', 'HMACSHA384']);
+  AddDefaultKeySizeEntries(512, ['HMACGOST3411-2012-512', 'HMACSHA3-512',
+    'HMACSHA512']);
+end;
+
+class constructor TGeneratorUtilities.CreateGeneratorUtilities;
+begin
+  TGeneratorUtilities.Boot;
+end;
+
+class destructor TGeneratorUtilities.DestroyGeneratorUtilities;
+begin
+  FkgAlgorithms.Free;
+  FkpgAlgorithms.Free;
+  FdefaultKeySizes.Free;
+end;
+
+class function TGeneratorUtilities.FindDefaultKeySize(const canonicalName
+  : String): Int32;
+begin
+  if (not FdefaultKeySizes.ContainsKey(canonicalName)) then
+  begin
+    result := -1;
+    Exit;
+  end;
+
+  FdefaultKeySizes.TryGetValue(canonicalName, result);
+end;
+
+class function TGeneratorUtilities.GetCanonicalKeyGeneratorAlgorithm
+  (const algorithm: String): String;
+begin
+  FkgAlgorithms.TryGetValue(UpperCase(algorithm), result);
+end;
+
+class function TGeneratorUtilities.GetCanonicalKeyPairGeneratorAlgorithm
+  (const algorithm: String): String;
+begin
+  FkpgAlgorithms.TryGetValue(UpperCase(algorithm), result);
+end;
+
+class function TGeneratorUtilities.GetDefaultKeySize(const algorithm
+  : String): Int32;
+var
+  canonicalName: string;
+  defaultKeySize: Int32;
+begin
+  canonicalName := GetCanonicalKeyGeneratorAlgorithm(algorithm);
+
+  if (canonicalName = '') then
+  begin
+    raise ESecurityUtilityCryptoLibException.CreateResFmt
+      (@SKeyGeneratorAlgorithmNotRecognised, [algorithm]);
+  end;
+
+  defaultKeySize := FindDefaultKeySize(canonicalName);
+  if (defaultKeySize = -1) then
+  begin
+
+    raise ESecurityUtilityCryptoLibException.CreateResFmt
+      (@SKeyGeneratorAlgorithmNotSupported, [algorithm, canonicalName]);
+  end;
+
+  result := defaultKeySize;
+end;
+
+class function TGeneratorUtilities.GetDefaultKeySize
+  (const oid: IDerObjectIdentifier): Int32;
+begin
+  result := GetDefaultKeySize(oid.ID);
+end;
+
+class function TGeneratorUtilities.GetKeyGenerator(const algorithm: String)
+  : ICipherKeyGenerator;
+var
+  canonicalName: string;
+  defaultKeySize: Int32;
+begin
+
+  canonicalName := GetCanonicalKeyGeneratorAlgorithm(algorithm);
+  if (canonicalName = '') then
+  begin
+    raise ESecurityUtilityCryptoLibException.CreateResFmt
+      (@SKeyGeneratorAlgorithmNotRecognised, [algorithm]);
+  end;
+
+  defaultKeySize := FindDefaultKeySize(canonicalName);
+  if (defaultKeySize = -1) then
+  begin
+    raise ESecurityUtilityCryptoLibException.CreateResFmt
+      (@SKeyGeneratorAlgorithmNotSupported, [algorithm, canonicalName]);
+  end;
+
+  result := TCipherKeyGenerator.Create(defaultKeySize);
+end;
+
+class function TGeneratorUtilities.GetKeyPairGenerator(const algorithm: String)
+  : IAsymmetricCipherKeyPairGenerator;
+var
+  canonicalName: string;
+begin
+  canonicalName := GetCanonicalKeyPairGeneratorAlgorithm(algorithm);
+
+  if (canonicalName = '') then
+  begin
+    raise ESecurityUtilityCryptoLibException.CreateResFmt
+      (@SKeyPairGeneratorAlgorithmNotRecognised, [algorithm]);
+  end;
+
+  if (canonicalName = 'DSA') then
+  begin
+    result := TDsaKeyPairGenerator.Create() as IDsaKeyPairGenerator;
+    Exit;
+  end;
+
+  // "EC", "ECDH", "ECDHC", "ECDSA", "ECGOST3410", "ECMQV"
+  if TStringUtils.BeginsWith(canonicalName, 'EC', True) then
+  begin
+    result := TECKeyPairGenerator.Create(canonicalName) as IECKeyPairGenerator;
+    Exit;
+  end;
+
+  raise ESecurityUtilityCryptoLibException.CreateResFmt
+    (@SKeyPairGeneratorAlgorithmNotSupported, [algorithm, canonicalName]);
+
+end;
+
+class function TGeneratorUtilities.GetKeyPairGenerator
+  (const oid: IDerObjectIdentifier): IAsymmetricCipherKeyPairGenerator;
+begin
+  result := GetKeyPairGenerator(oid.ID);
+end;
+
+end.

+ 133 - 0
src/libraries/cryptolib4pascal/ClpGenericPolynomialExtensionField.pas

@@ -0,0 +1,133 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpGenericPolynomialExtensionField;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpBits,
+  ClpBigInteger,
+  ClpIFiniteField,
+  ClpIPolynomial,
+  ClpIGenericPolynomialExtensionField,
+  ClpIPolynomialExtensionField;
+
+type
+  TGenericPolynomialExtensionField = class(TInterfacedObject,
+    IPolynomialExtensionField, IGenericPolynomialExtensionField)
+
+  strict private
+    function GetCharacteristic: TBigInteger; virtual;
+    function GetDegree: Int32; virtual;
+    function GetDimension: Int32; virtual;
+    function GetMinimalPolynomial: IPolynomial; virtual;
+    function GetSubField: IFiniteField; virtual;
+  strict protected
+  var
+    Fsubfield: IFiniteField;
+    FminimalPolynomial: IPolynomial;
+
+    property Characteristic: TBigInteger read GetCharacteristic;
+    property Dimension: Int32 read GetDimension;
+    property subfield: IFiniteField read GetSubField;
+    property Degree: Int32 read GetDegree;
+    property MinimalPolynomial: IPolynomial read GetMinimalPolynomial;
+
+  public
+    constructor Create(const subfield: IFiniteField;
+      const polynomial: IPolynomial);
+
+    function Equals(other: TObject): Boolean; overload; override;
+    function Equals(const other: IGenericPolynomialExtensionField): Boolean;
+      reintroduce; overload;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+  end;
+
+implementation
+
+{ TGenericPolynomialExtensionField }
+
+constructor TGenericPolynomialExtensionField.Create(const subfield
+  : IFiniteField; const polynomial: IPolynomial);
+begin
+  Fsubfield := subfield;
+  FminimalPolynomial := polynomial;
+end;
+
+function TGenericPolynomialExtensionField.Equals(const other
+  : IGenericPolynomialExtensionField): Boolean;
+begin
+  if ((Self as IGenericPolynomialExtensionField) = other) then
+  begin
+    Result := true;
+    Exit;
+  end;
+
+  if (other = Nil) then
+  begin
+    Result := false;
+    Exit;
+  end;
+  Result := (subfield as TObject).Equals(other.subfield as TObject) and
+    (MinimalPolynomial as TObject).Equals(other.MinimalPolynomial as TObject);
+end;
+
+function TGenericPolynomialExtensionField.Equals(other: TObject): Boolean;
+begin
+  Result := Self.Equals((other as TGenericPolynomialExtensionField)
+    as IGenericPolynomialExtensionField);
+end;
+
+function TGenericPolynomialExtensionField.GetCharacteristic: TBigInteger;
+begin
+  Result := Fsubfield.Characteristic;
+end;
+
+function TGenericPolynomialExtensionField.GetDegree: Int32;
+begin
+  Result := FminimalPolynomial.Degree;
+end;
+
+function TGenericPolynomialExtensionField.GetDimension: Int32;
+begin
+  Result := Fsubfield.Dimension * FminimalPolynomial.Degree;
+end;
+
+function TGenericPolynomialExtensionField.GetHashCode: {$IFDEF DELPHI}Int32;
+{$ELSE}PtrInt; {$ENDIF DELPHI}
+begin
+  Result := (subfield as TObject).GetHashCode()
+    xor Int32(TBits.RotateLeft32((MinimalPolynomial as TObject)
+    .GetHashCode(), 16));
+end;
+
+function TGenericPolynomialExtensionField.GetMinimalPolynomial: IPolynomial;
+begin
+  Result := FminimalPolynomial;
+end;
+
+function TGenericPolynomialExtensionField.GetSubField: IFiniteField;
+begin
+  Result := Fsubfield;
+end;
+
+end.

+ 106 - 0
src/libraries/cryptolib4pascal/ClpGlvMultiplier.pas

@@ -0,0 +1,106 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpGlvMultiplier;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpSetWeakRef,
+  ClpAbstractECMultiplier,
+  ClpIECC,
+  ClpIGlvEndomorphism,
+  ClpCryptoLibTypes,
+  ClpECAlgorithms,
+  ClpBigInteger,
+  ClpIGlvMultiplier;
+
+resourcestring
+  SCurveUnknownGroupOrder = 'Need Curve With Known Group Order, "curve"';
+
+type
+  TGlvMultiplier = class(TAbstractECMultiplier, IGlvMultiplier)
+
+  strict protected
+  var
+    Fcurve: IECCurve;
+    FglvEndomorphism: IGlvEndomorphism;
+
+    function MultiplyPositive(const p: IECPoint; const k: TBigInteger)
+      : IECPoint; override;
+
+  public
+    constructor Create(const curve: IECCurve;
+      const glvEndomorphism: IGlvEndomorphism);
+    destructor Destroy; override;
+
+  end;
+
+implementation
+
+{ TGlvMultiplier }
+
+constructor TGlvMultiplier.Create(const curve: IECCurve;
+  const glvEndomorphism: IGlvEndomorphism);
+begin
+  inherited Create();
+  if ((curve = Nil) or (not(curve.Order.IsInitialized))) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SCurveUnknownGroupOrder);
+  end;
+
+  // Fcurve := curve;
+  TSetWeakRef.SetWeakReference(@Fcurve, curve);
+  FglvEndomorphism := glvEndomorphism;
+end;
+
+destructor TGlvMultiplier.Destroy;
+begin
+  TSetWeakRef.SetWeakReference(@Fcurve, Nil);
+  inherited Destroy;
+end;
+
+function TGlvMultiplier.MultiplyPositive(const p: IECPoint;
+  const k: TBigInteger): IECPoint;
+var
+  n, a, b: TBigInteger;
+  ab: TCryptoLibGenericArray<TBigInteger>;
+  pointMap: IECPointMap;
+begin
+  if (not(Fcurve.Equals(p.curve))) then
+  begin
+    raise EInvalidOperationCryptoLibException.Create('');
+  end;
+
+  n := p.curve.Order;
+  ab := FglvEndomorphism.DecomposeScalar(k.&Mod(n));
+  a := ab[0];
+  b := ab[1];
+
+  pointMap := FglvEndomorphism.pointMap;
+  if (FglvEndomorphism.HasEfficientPointMap) then
+  begin
+    Result := TECAlgorithms.ImplShamirsTrickWNaf(p, a, pointMap, b);
+    Exit;
+  end;
+
+  Result := TECAlgorithms.ImplShamirsTrickWNaf(p, a, pointMap.Map(p), b);
+end;
+
+end.

+ 132 - 0
src/libraries/cryptolib4pascal/ClpGlvTypeBEndomorphism.pas

@@ -0,0 +1,132 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpGlvTypeBEndomorphism;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpCryptoLibTypes,
+  ClpBigInteger,
+  ClpScaleXPointMap,
+  ClpIGlvTypeBEndomorphism,
+  ClpIECC,
+  ClpIGlvTypeBParameters,
+  ClpIGlvEndomorphism;
+
+type
+  TGlvTypeBEndomorphism = class(TInterfacedObject, IECEndomorphism,
+    IGlvEndomorphism, IGlvTypeBEndomorphism)
+
+  strict private
+    function GetHasEfficientPointMap: Boolean; virtual;
+    function GetPointMap: IECPointMap; virtual;
+
+  strict protected
+  var
+    Fm_parameters: IGlvTypeBParameters;
+    Fm_pointMap: IECPointMap;
+    Fm_curve: IECCurve;
+
+    function CalculateB(const k, g: TBigInteger; t: Int32)
+      : TBigInteger; virtual;
+
+  public
+    constructor Create(const curve: IECCurve;
+      const parameters: IGlvTypeBParameters);
+    destructor Destroy; override;
+    function DecomposeScalar(const k: TBigInteger)
+      : TCryptoLibGenericArray<TBigInteger>; virtual;
+
+    property PointMap: IECPointMap read GetPointMap;
+    property HasEfficientPointMap: Boolean read GetHasEfficientPointMap;
+  end;
+
+implementation
+
+{ TGlvTypeBEndomorphism }
+
+function TGlvTypeBEndomorphism.CalculateB(const k, g: TBigInteger; t: Int32)
+  : TBigInteger;
+var
+  negative, extra: Boolean;
+  b: TBigInteger;
+begin
+  negative := (g.SignValue < 0);
+  b := k.Multiply(g.Abs());
+  extra := b.TestBit(t - 1);
+  b := b.ShiftRight(t);
+  if (extra) then
+  begin
+    b := b.Add(TBigInteger.One);
+  end;
+  if negative then
+  begin
+    Result := b.Negate();
+  end
+  else
+  begin
+    Result := b;
+  end;
+end;
+
+constructor TGlvTypeBEndomorphism.Create(const curve: IECCurve;
+  const parameters: IGlvTypeBParameters);
+begin
+  Inherited Create();
+  Fm_curve := curve;
+  Fm_parameters := parameters;
+  Fm_pointMap := TScaleXPointMap.Create(curve.FromBigInteger(parameters.Beta));
+end;
+
+function TGlvTypeBEndomorphism.DecomposeScalar(const k: TBigInteger)
+  : TCryptoLibGenericArray<TBigInteger>;
+var
+  bits: Int32;
+  b1, b2, a, b: TBigInteger;
+  v1, v2: TCryptoLibGenericArray<TBigInteger>;
+begin
+  bits := Fm_parameters.bits;
+  b1 := CalculateB(k, Fm_parameters.G1, bits);
+  b2 := CalculateB(k, Fm_parameters.G2, bits);
+
+  v1 := Fm_parameters.v1;
+  v2 := Fm_parameters.v2;
+  a := k.Subtract((b1.Multiply(v1[0])).Add(b2.Multiply(v2[0])));
+  b := (b1.Multiply(v1[1])).Add(b2.Multiply(v2[1])).Negate();
+
+  Result := TCryptoLibGenericArray<TBigInteger>.Create(a, b);
+end;
+
+destructor TGlvTypeBEndomorphism.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TGlvTypeBEndomorphism.GetHasEfficientPointMap: Boolean;
+begin
+  Result := true;
+end;
+
+function TGlvTypeBEndomorphism.GetPointMap: IECPointMap;
+begin
+  Result := Fm_pointMap;
+end;
+
+end.

+ 120 - 0
src/libraries/cryptolib4pascal/ClpGlvTypeBParameters.pas

@@ -0,0 +1,120 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpGlvTypeBParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpBigInteger,
+  ClpIGlvTypeBParameters,
+  ClpCryptoLibTypes;
+
+type
+  TGlvTypeBParameters = class sealed(TInterfacedObject, IGlvTypeBParameters)
+
+  strict private
+    function GetBeta: TBigInteger; inline;
+    function GetBits: Int32; inline;
+    function GetG1: TBigInteger; inline;
+    function GetG2: TBigInteger; inline;
+    function GetLambda: TBigInteger; inline;
+    function GetV1: TCryptoLibGenericArray<TBigInteger>; inline;
+    function GetV2: TCryptoLibGenericArray<TBigInteger>; inline;
+  strict protected
+    Fm_beta, Fm_lambda: TBigInteger;
+    Fm_v1, Fm_v2: TCryptoLibGenericArray<TBigInteger>;
+    Fm_g1, Fm_g2: TBigInteger;
+    Fm_bits: Int32;
+
+  public
+    constructor Create(const beta, lambda: TBigInteger;
+      const v1, v2: TCryptoLibGenericArray<TBigInteger>;
+      const g1, g2: TBigInteger; bits: Int32);
+
+    destructor Destroy; override;
+
+    property beta: TBigInteger read GetBeta;
+    property lambda: TBigInteger read GetLambda;
+    property v1: TCryptoLibGenericArray<TBigInteger> read GetV1;
+    property v2: TCryptoLibGenericArray<TBigInteger> read GetV2;
+    property g1: TBigInteger read GetG1;
+    property g2: TBigInteger read GetG2;
+    property bits: Int32 read GetBits;
+
+  end;
+
+implementation
+
+{ TGlvTypeBParameters }
+
+constructor TGlvTypeBParameters.Create(const beta, lambda: TBigInteger;
+  const v1, v2: TCryptoLibGenericArray<TBigInteger>; const g1, g2: TBigInteger;
+  bits: Int32);
+begin
+  Fm_beta := beta;
+  Fm_lambda := lambda;
+  Fm_v1 := v1;
+  Fm_v2 := v2;
+  Fm_g1 := g1;
+  Fm_g2 := g2;
+  Fm_bits := bits;
+end;
+
+destructor TGlvTypeBParameters.Destroy;
+begin
+  inherited Destroy;
+end;
+
+function TGlvTypeBParameters.GetBeta: TBigInteger;
+begin
+  Result := Fm_beta;
+end;
+
+function TGlvTypeBParameters.GetBits: Int32;
+begin
+  Result := Fm_bits;
+end;
+
+function TGlvTypeBParameters.GetG1: TBigInteger;
+begin
+  Result := Fm_g1;
+end;
+
+function TGlvTypeBParameters.GetG2: TBigInteger;
+begin
+  Result := Fm_g2;
+end;
+
+function TGlvTypeBParameters.GetLambda: TBigInteger;
+begin
+  Result := Fm_lambda;
+end;
+
+function TGlvTypeBParameters.GetV1: TCryptoLibGenericArray<TBigInteger>;
+begin
+  Result := Fm_v1;
+end;
+
+function TGlvTypeBParameters.GetV2: TCryptoLibGenericArray<TBigInteger>;
+begin
+  Result := Fm_v2;
+end;
+
+end.

+ 150 - 0
src/libraries/cryptolib4pascal/ClpHMac.pas

@@ -0,0 +1,150 @@
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpHMac;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  HlpIHashInfo,
+  HlpHashFactory,
+  ClpIMac,
+  ClpIHMac,
+  ClpIDigest,
+  ClpIKeyParameter,
+  ClpICipherParameters,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SOutputBufferTooShort = 'Output Buffer Too Short';
+
+type
+
+  /// <summary>
+  /// <para>
+  /// HMAC implementation based on RFC2104 <br />H(K XOR opad, H(K XOR
+  /// ipad, text))
+  /// </para>
+  /// <para>
+  /// Note: This is Just a Wrapper for <b>HMAC</b> Implementation in
+  /// HashLib4Pascal
+  /// </para>
+  /// </summary>
+  THMac = class sealed(TInterfacedObject, IHMac, IMac)
+
+  strict private
+  var
+    FDigest: IDigest;
+    FHMAC: HlpIHashInfo.IHMac;
+
+    function GetAlgorithmName: string; inline;
+
+  public
+    constructor Create(const digest: IDigest);
+
+    function GetUnderlyingDigest: IDigest; inline;
+    function GetMacSize: Int32; inline;
+
+    procedure Update(input: Byte);
+    procedure BlockUpdate(const input: TCryptoLibByteArray; inOff, len: Int32);
+    procedure Init(const parameters: ICipherParameters);
+    function DoFinal(const output: TCryptoLibByteArray; outOff: Int32)
+      : Int32; overload;
+    function DoFinal: TCryptoLibByteArray; overload;
+
+    /// <summary>
+    /// Reset the mac generator.
+    /// </summary>
+    procedure Reset();
+
+    property AlgorithmName: String read GetAlgorithmName;
+
+  end;
+
+implementation
+
+{ THMac }
+
+function THMac.GetMacSize: Int32;
+begin
+  result := FHMAC.HashSize;
+end;
+
+procedure THMac.BlockUpdate(const input: TCryptoLibByteArray;
+  inOff, len: Int32);
+begin
+  FHMAC.TransformBytes(input, inOff, len);
+end;
+
+constructor THMac.Create(const digest: IDigest);
+begin
+  Inherited Create();
+  FDigest := digest;
+  FHMAC := THashFactory.THMac.CreateHMAC(FDigest.GetUnderlyingIHash);
+end;
+
+function THMac.DoFinal(const output: TCryptoLibByteArray; outOff: Int32): Int32;
+var
+  buf: TCryptoLibByteArray;
+begin
+
+  if (System.Length(output) - outOff) < GetMacSize then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
+  end
+  else
+  begin
+    buf := DoFinal();
+    System.Move(buf[0], output[outOff], System.Length(buf) *
+      System.SizeOf(Byte));
+  end;
+  result := System.Length(buf);
+end;
+
+function THMac.DoFinal: TCryptoLibByteArray;
+begin
+  result := FHMAC.TransformFinal.GetBytes();
+end;
+
+function THMac.GetAlgorithmName: string;
+begin
+  result := FDigest.AlgorithmName + '/HMAC';
+end;
+
+function THMac.GetUnderlyingDigest: IDigest;
+begin
+  result := FDigest;
+end;
+
+procedure THMac.Init(const parameters: ICipherParameters);
+begin
+  FHMAC.Key := (parameters as IKeyParameter).GetKey();
+  FHMAC.Initialize;
+end;
+
+procedure THMac.Reset;
+begin
+  FHMAC.Initialize;
+end;
+
+procedure THMac.Update(input: Byte);
+begin
+  FHMAC.TransformUntyped(input, System.SizeOf(Byte));
+end;
+
+end.

+ 228 - 0
src/libraries/cryptolib4pascal/ClpHMacDsaKCalculator.pas

@@ -0,0 +1,228 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpHMacDsaKCalculator;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Math,
+  ClpHMac,
+  ClpIHMac,
+  ClpIDigest,
+  ClpISecureRandom,
+  ClpBigInteger,
+  ClpBigIntegers,
+  ClpKeyParameter,
+  ClpIKeyParameter,
+  ClpIDsaKCalculator,
+  ClpIHMacDsaKCalculator,
+  ClpArrayUtils,
+  ClpCryptoLibTypes;
+
+{$IFNDEF _FIXINSIGHT_}
+
+resourcestring
+  SUnSupportedOperation = 'Operation not Supported';
+{$ENDIF}
+
+type
+
+  /// <summary>
+  /// A deterministic K calculator based on the algorithm in section 3.2 of
+  /// RFC 6979.
+  /// </summary>
+  THMacDsaKCalculator = class(TInterfacedObject, IDsaKCalculator,
+    IHMacDsaKCalculator)
+
+  strict private
+  var
+    FhMac: IHMac;
+    FK, FV: TCryptoLibByteArray;
+    Fn: TBigInteger;
+
+    function BitsToInt(const t: TCryptoLibByteArray): TBigInteger; inline;
+
+    function GetIsDeterministic: Boolean; virtual;
+
+  public
+
+    /// <summary>
+    /// Base constructor.
+    /// </summary>
+    /// <param name="digest">
+    /// digest to build the HMAC on.
+    /// </param>
+    constructor Create(const digest: IDigest);
+
+    procedure Init(const n: TBigInteger; const random: ISecureRandom);
+      overload; virtual;
+
+    procedure Init(const n, d: TBigInteger;
+      const &message: TCryptoLibByteArray); overload;
+
+    function NextK(): TBigInteger; virtual;
+
+    property IsDeterministic: Boolean read GetIsDeterministic;
+
+  end;
+
+implementation
+
+{ THMacDsaKCalculator }
+
+function THMacDsaKCalculator.GetIsDeterministic: Boolean;
+begin
+  result := True;
+end;
+
+function THMacDsaKCalculator.BitsToInt(const t: TCryptoLibByteArray)
+  : TBigInteger;
+begin
+  result := TBigInteger.Create(1, t);
+  if ((System.Length(t) * 8) > Fn.BitLength) then
+  begin
+    result := result.ShiftRight((System.Length(t) * 8) - Fn.BitLength);
+  end;
+end;
+
+constructor THMacDsaKCalculator.Create(const digest: IDigest);
+begin
+  Inherited Create();
+  FhMac := THMac.Create(digest);
+  System.SetLength(FV, FhMac.GetMacSize());
+  System.SetLength(FK, FhMac.GetMacSize());
+end;
+
+{$IFNDEF _FIXINSIGHT_}
+
+procedure THMacDsaKCalculator.Init(const n: TBigInteger;
+  const random: ISecureRandom);
+begin
+  raise EInvalidOperationCryptoLibException.CreateRes(@SUnSupportedOperation);
+end;
+{$ENDIF}
+
+procedure THMacDsaKCalculator.Init(const n, d: TBigInteger;
+  const &message: TCryptoLibByteArray);
+var
+  x, dVal, m, mVal: TCryptoLibByteArray;
+  mInt: TBigInteger;
+  size: Int32;
+begin
+  Fn := n;
+  TArrayUtils.Fill(FV, 0, System.Length(FV), Byte($01));
+  TArrayUtils.Fill(FK, 0, System.Length(FK), Byte(0));
+
+  size := TBigIntegers.GetUnsignedByteLength(n);
+  System.SetLength(x, size);
+
+  dVal := TBigIntegers.AsUnsignedByteArray(d);
+
+  System.Move(dVal[0], x[System.Length(x) - System.Length(dVal)],
+    System.Length(dVal));
+
+  System.SetLength(m, size);
+
+  mInt := BitsToInt(&message);
+
+  if (mInt.CompareTo(n) >= 0) then
+  begin
+    mInt := mInt.Subtract(n);
+  end;
+
+  mVal := TBigIntegers.AsUnsignedByteArray(mInt);
+
+  System.Move(mVal[0], m[System.Length(m) - System.Length(mVal)],
+    System.Length(mVal));
+
+  FhMac.Init(TKeyParameter.Create(FK) as IKeyParameter);
+
+  FhMac.BlockUpdate(FV, 0, System.Length(FV));
+  FhMac.Update(Byte($00));
+  FhMac.BlockUpdate(x, 0, System.Length(x));
+  FhMac.BlockUpdate(m, 0, System.Length(m));
+
+  FhMac.DoFinal(FK, 0);
+
+  FhMac.Init(TKeyParameter.Create(FK) as IKeyParameter);
+
+  FhMac.BlockUpdate(FV, 0, System.Length(FV));
+
+  FhMac.DoFinal(FV, 0);
+
+  FhMac.BlockUpdate(FV, 0, System.Length(FV));
+  FhMac.Update(Byte($01));
+  FhMac.BlockUpdate(x, 0, System.Length(x));
+  FhMac.BlockUpdate(m, 0, System.Length(m));
+
+  FhMac.DoFinal(FK, 0);
+
+  FhMac.Init(TKeyParameter.Create(FK) as IKeyParameter);
+
+  FhMac.BlockUpdate(FV, 0, System.Length(FV));
+
+  FhMac.DoFinal(FV, 0);
+end;
+
+function THMacDsaKCalculator.NextK: TBigInteger;
+var
+  t: TCryptoLibByteArray;
+  tOff, len: Int32;
+begin
+  result := Default (TBigInteger);
+  System.SetLength(t, TBigIntegers.GetUnsignedByteLength(Fn));
+
+  while True do
+
+  begin
+    tOff := 0;
+
+    while (tOff < System.Length(t)) do
+    begin
+      FhMac.BlockUpdate(FV, 0, System.Length(FV));
+
+      FhMac.DoFinal(FV, 0);
+
+      len := Min(System.Length(t) - tOff, System.Length(FV));
+      System.Move(FV[0], t[tOff], len * System.SizeOf(Byte));
+      tOff := tOff + len;
+    end;
+
+    result := BitsToInt(t);
+
+    if ((result.SignValue > 0) and (result.CompareTo(Fn) < 0)) then
+    begin
+      Exit;
+    end;
+
+    FhMac.BlockUpdate(FV, 0, System.Length(FV));
+    FhMac.Update(Byte($00));
+
+    FhMac.DoFinal(FK, 0);
+
+    FhMac.Init(TKeyParameter.Create(FK) as IKeyParameter);
+
+    FhMac.BlockUpdate(FV, 0, System.Length(FV));
+
+    FhMac.DoFinal(FV, 0);
+  end;
+end;
+
+end.

+ 231 - 0
src/libraries/cryptolib4pascal/ClpHkdfBytesGenerator.pas

@@ -0,0 +1,231 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpHkdfBytesGenerator;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Math,
+  SysUtils,
+  ClpHMac,
+  ClpIHMac,
+  ClpIDigest,
+  ClpKeyParameter,
+  ClpIKeyParameter,
+  ClpIHkdfParameters,
+  ClpIHkdfBytesGenerator,
+  ClpIDerivationFunction,
+  ClpIDerivationParameters,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SSizeTooBigHKDF = 'HKDF Cannot Generate More Than 255 Blocks of HashLen Size';
+  SSizeTooBigHKDF2 = 'HKDF May Only Be Used For 255 * HashLen Bytes of Output';
+  SInvalidParameterHKDF =
+    'HKDF Parameters Required For "HkdfBytesGenerator", "parameters"';
+
+type
+
+  /// <summary>
+  /// HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
+  /// implemented <br />according to IETF RFC 5869, May 2010 as specified by
+  /// H. Krawczyk, IBM <br />Research &amp;amp; P. Eronen, Nokia. It uses a
+  /// HMac internally to compute the OKM <br />(output keying material) and
+  /// is likely to have better security properties <br />than KDF's based on
+  /// just a hash function.
+  /// </summary>
+  THkdfBytesGenerator = class(TInterfacedObject, IDerivationFunction,
+    IHkdfBytesGenerator)
+
+  strict private
+  var
+    FhMacHash: IHMac;
+    FhashLen, FgeneratedBytes: Int32;
+    Finfo, FcurrentT: TCryptoLibByteArray;
+
+    /// <summary>
+    /// Performs the extract part of the key derivation function.
+    /// </summary>
+    /// <param name="salt">
+    /// the salt to use
+    /// </param>
+    /// <param name="ikm">
+    /// the input keying material
+    /// </param>
+    /// <returns>
+    /// the PRK as KeyParameter
+    /// </returns>
+    function Extract(const salt, ikm: TCryptoLibByteArray): IKeyParameter;
+
+    /// <summary>
+    /// Performs the expand part of the key derivation function, using
+    /// currentT <br />as input and output buffer.
+    /// </summary>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// if the total number of bytes generated is larger than the one
+    /// specified by RFC 5869 (255 * HashLen)
+    /// </exception>
+    procedure ExpandNext();
+
+  strict protected
+    function GetDigest: IDigest; virtual;
+
+  public
+
+    /// <summary>
+    /// Creates a HKDFBytesGenerator based on the given hash function.
+    /// </summary>
+    /// <param name="hash">
+    /// the digest to be used as the source of generatedBytes bytes
+    /// </param>
+    constructor Create(const hash: IDigest);
+
+    procedure Init(const parameters: IDerivationParameters); virtual;
+
+    function GenerateBytes(const output: TCryptoLibByteArray;
+      outOff, len: Int32): Int32; virtual;
+
+    property Digest: IDigest read GetDigest;
+
+  end;
+
+implementation
+
+{ THkdfBytesGenerator }
+
+constructor THkdfBytesGenerator.Create(const hash: IDigest);
+begin
+  Inherited Create();
+  FhMacHash := THMac.Create(hash);
+  FhashLen := hash.GetDigestSize();
+end;
+
+procedure THkdfBytesGenerator.ExpandNext;
+var
+  n: Int32;
+begin
+  n := (FgeneratedBytes div FhashLen) + 1;
+  if (n >= 256) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SSizeTooBigHKDF);
+  end;
+  // special case for T(0): T(0) is empty, so no update
+  if (FgeneratedBytes <> 0) then
+  begin
+    FhMacHash.BlockUpdate(FcurrentT, 0, FhashLen);
+  end;
+  FhMacHash.BlockUpdate(Finfo, 0, System.Length(Finfo));
+  FhMacHash.Update(Byte(n));
+  FhMacHash.DoFinal(FcurrentT, 0);
+end;
+
+function THkdfBytesGenerator.Extract(const salt, ikm: TCryptoLibByteArray)
+  : IKeyParameter;
+var
+  temp, prk: TCryptoLibByteArray;
+begin
+  if (salt = Nil) then
+  begin
+    System.SetLength(temp, FhashLen);
+    // TODO check if hashLen is indeed same as HMAC size
+    FhMacHash.Init(TKeyParameter.Create(temp) as IKeyParameter);
+  end
+  else
+  begin
+    FhMacHash.Init(TKeyParameter.Create(salt) as IKeyParameter);
+  end;
+
+  FhMacHash.BlockUpdate(ikm, 0, System.Length(ikm));
+
+  System.SetLength(prk, FhashLen);
+  FhMacHash.DoFinal(prk, 0);
+  result := TKeyParameter.Create(prk);
+end;
+
+function THkdfBytesGenerator.GenerateBytes(const output: TCryptoLibByteArray;
+  outOff, len: Int32): Int32;
+var
+  toGenerate, posInT, leftInT, toCopy: Int32;
+begin
+  if ((FgeneratedBytes + len) > (255 * FhashLen)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SSizeTooBigHKDF2);
+  end;
+
+  if (FgeneratedBytes mod FhashLen = 0) then
+  begin
+    ExpandNext();
+  end;
+
+  // copy what is left in the currentT (1..hash
+  toGenerate := len;
+  posInT := FgeneratedBytes mod FhashLen;
+  leftInT := FhashLen - (FgeneratedBytes mod FhashLen);
+  toCopy := Min(leftInT, toGenerate);
+  System.Move(FcurrentT[posInT], output[outOff], toCopy);
+  FgeneratedBytes := FgeneratedBytes + toCopy;
+  toGenerate := toGenerate - toCopy;
+  outOff := outOff + toCopy;
+
+  while (toGenerate > 0) do
+  begin
+    ExpandNext();
+    toCopy := Min(FhashLen, toGenerate);
+    System.Move(FcurrentT[0], output[outOff], toCopy);
+    FgeneratedBytes := FgeneratedBytes + toCopy;
+    toGenerate := toGenerate - toCopy;
+    outOff := outOff + toCopy;
+  end;
+
+  result := len;
+end;
+
+function THkdfBytesGenerator.GetDigest: IDigest;
+begin
+  result := FhMacHash.GetUnderlyingDigest();
+end;
+
+procedure THkdfBytesGenerator.Init(const parameters: IDerivationParameters);
+var
+  hkdfParameters: IHkdfParameters;
+begin
+  if (not Supports(parameters, IHkdfParameters, hkdfParameters)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidParameterHKDF);
+  end;
+
+  if (hkdfParameters.SkipExtract) then
+  begin
+    // use IKM directly as PRK
+    FhMacHash.Init(TKeyParameter.Create(hkdfParameters.GetIkm())
+      as IKeyParameter);
+  end
+  else
+  begin
+    FhMacHash.Init(Extract(hkdfParameters.GetSalt(), hkdfParameters.GetIkm()));
+  end;
+
+  Finfo := hkdfParameters.GetInfo();
+
+  FgeneratedBytes := 0;
+  System.SetLength(FcurrentT, FhashLen);
+end;
+
+end.

+ 203 - 0
src/libraries/cryptolib4pascal/ClpHkdfParameters.pas

@@ -0,0 +1,203 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpHkdfParameters;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIHkdfParameters,
+  ClpIDerivationParameters,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SIKMNil = '"ikm" is Nil';
+
+type
+
+  /// <summary>
+  /// Parameter class for the HkdfBytesGenerator class.
+  /// </summary>
+  THkdfParameters = class(TInterfacedObject, IDerivationParameters,
+    IHkdfParameters)
+
+  strict private
+  var
+    Fikm, Fsalt, Finfo: TCryptoLibByteArray;
+    FskipExpand: Boolean;
+
+    constructor Create(const ikm: TCryptoLibByteArray; skip: Boolean;
+      const salt, info: TCryptoLibByteArray); overload;
+
+  strict protected
+
+    /// <summary>
+    /// Returns if step 1: extract has to be skipped or not
+    /// </summary>
+    /// <returns>
+    /// true for skipping, false for no skipping of step 1
+    /// </returns>
+    function GetSkipExtract: Boolean; virtual;
+
+  public
+
+    /// <summary>
+    /// Generates parameters for HKDF, specifying both the optional salt and <br />
+    /// optional info. Step 1: Extract won't be skipped.
+    /// </summary>
+    /// <param name="ikm">
+    /// the input keying material or seed
+    /// </param>
+    /// <param name="salt">
+    /// the salt to use, may be null for a salt for hashLen zeros
+    /// </param>
+    /// <param name="info">
+    /// the info to use, may be null for an info field of zero bytes
+    /// </param>
+    constructor Create(const ikm, salt, info: TCryptoLibByteArray); overload;
+
+    /// <summary>
+    /// Returns the input keying material or seed.
+    /// </summary>
+    /// <returns>
+    /// the keying material
+    /// </returns>
+    function GetIkm(): TCryptoLibByteArray; virtual;
+
+    /// <summary>
+    /// Returns the salt, or null if the salt should be generated as a byte
+    /// array <br />of HashLen zeros.
+    /// </summary>
+    /// <returns>
+    /// the salt, or null
+    /// </returns>
+    function GetSalt(): TCryptoLibByteArray; virtual;
+
+    /// <summary>
+    /// Returns the info field, which may be empty (null is converted to
+    /// empty).
+    /// </summary>
+    /// <returns>
+    /// the info field, never null
+    /// </returns>
+    function GetInfo(): TCryptoLibByteArray; virtual;
+
+    /// <summary>
+    /// Returns if step 1: extract has to be skipped or not
+    /// </summary>
+    /// <returns>
+    /// true for skipping, false for no skipping of step 1
+    /// </returns>
+    property SkipExtract: Boolean read GetSkipExtract;
+
+    /// <summary>
+    /// Factory method that makes the HKDF skip the extract part of the key <br />
+    /// derivation function.
+    /// </summary>
+    /// <param name="ikm">
+    /// the input keying material or seed, directly used for step 2: Expand
+    /// </param>
+    /// <param name="info">
+    /// the info to use, may be null for an info field of zero bytes
+    /// </param>
+    /// <returns>
+    /// that makes the implementation skip step 1
+    /// </returns>
+    class function SkipExtractParameters(const ikm, info: TCryptoLibByteArray)
+      : IHkdfParameters; static; inline;
+
+    class function DefaultParameters(const ikm: TCryptoLibByteArray)
+      : IHkdfParameters; static; inline;
+
+  end;
+
+implementation
+
+{ THkdfParameters }
+
+constructor THkdfParameters.Create(const ikm: TCryptoLibByteArray;
+  skip: Boolean; const salt, info: TCryptoLibByteArray);
+begin
+  Inherited Create();
+
+  if (ikm = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SIKMNil);
+  end;
+
+  Fikm := System.Copy(ikm);
+  FskipExpand := skip;
+
+  if ((salt = Nil) or (System.Length(salt) = 0)) then
+  begin
+    Fsalt := Nil;
+  end
+  else
+  begin
+    Fsalt := System.Copy(salt);
+  end;
+
+  if (info = Nil) then
+  begin
+    System.SetLength(Finfo, 0);
+  end
+  else
+  begin
+    Finfo := System.Copy(info);
+  end;
+end;
+
+constructor THkdfParameters.Create(const ikm, salt, info: TCryptoLibByteArray);
+begin
+  Create(ikm, false, salt, info);
+end;
+
+class function THkdfParameters.DefaultParameters(const ikm: TCryptoLibByteArray)
+  : IHkdfParameters;
+begin
+  result := THkdfParameters.Create(ikm, false, Nil, Nil);
+end;
+
+function THkdfParameters.GetIkm: TCryptoLibByteArray;
+begin
+  result := System.Copy(Fikm);
+end;
+
+function THkdfParameters.GetInfo: TCryptoLibByteArray;
+begin
+  result := System.Copy(Finfo);
+end;
+
+function THkdfParameters.GetSalt: TCryptoLibByteArray;
+begin
+  result := System.Copy(Fsalt);
+end;
+
+function THkdfParameters.GetSkipExtract: Boolean;
+begin
+  result := FskipExpand;
+end;
+
+class function THkdfParameters.SkipExtractParameters(const ikm,
+  info: TCryptoLibByteArray): IHkdfParameters;
+begin
+  result := THkdfParameters.Create(ikm, true, Nil, info);
+end;
+
+end.

+ 40 - 0
src/libraries/cryptolib4pascal/ClpIAbstractECMultiplier.pas

@@ -0,0 +1,40 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIAbstractECMultiplier;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpBigInteger,
+  ClpIECC;
+
+type
+
+  IAbstractECMultiplier = interface(IECMultiplier)
+    ['{DD63984C-7D4D-46DE-9004-20FD909C2EFB}']
+
+    function MultiplyPositive(const p: IECPoint; const k: TBigInteger)
+      : IECPoint;
+
+  end;
+
+implementation
+
+end.

+ 36 - 0
src/libraries/cryptolib4pascal/ClpIAesEngine.pas

@@ -0,0 +1,36 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIAesEngine;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIBlockCipher;
+
+type
+
+  IAesEngine = interface(IBlockCipher)
+    ['{984D6EC6-DBFC-4CEC-88B6-29B1C0BEA6CD}']
+
+  end;
+
+implementation
+
+end.

+ 36 - 0
src/libraries/cryptolib4pascal/ClpIAesLightEngine.pas

@@ -0,0 +1,36 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIAesLightEngine;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIBlockCipher;
+
+type
+
+  IAesLightEngine = interface(IBlockCipher)
+    ['{DC3E80FD-A8C7-454D-9727-C8E68B006BA6}']
+
+  end;
+
+implementation
+
+end.

+ 765 - 0
src/libraries/cryptolib4pascal/ClpIAsn1Objects.pas

@@ -0,0 +1,765 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIAsn1Objects;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpBigInteger,
+  Generics.Collections,
+  ClpCryptoLibTypes;
+
+type
+  IAsn1Object = interface;
+
+  IAsn1Convertible = interface(IInterface)
+    ['{13104D9E-9DF1-4CCE-B48C-1ACC2AC362B1}']
+
+    function ToAsn1Object(): IAsn1Object;
+  end;
+
+  IAsn1Encodable = interface(IAsn1Convertible)
+
+    ['{1B2D1F84-4E8F-442E-86F8-B75C9942F1AB}']
+
+    function GetEncoded(): TCryptoLibByteArray; overload;
+    function GetEncoded(const encoding: String): TCryptoLibByteArray; overload;
+
+    function GetDerEncoded(): TCryptoLibByteArray;
+
+    function Equals(const obj: IAsn1Convertible): Boolean;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+  end;
+
+  IAsn1Object = interface(IAsn1Encodable)
+
+    ['{83A52A0F-570B-43BB-9B98-8E5351FDA996}']
+
+    function Asn1Equals(const asn1Object: IAsn1Object): Boolean;
+
+    function Asn1GetHashCode(): Int32;
+
+    procedure Encode(const derOut: TStream);
+
+    function CallAsn1Equals(const obj: IAsn1Object): Boolean;
+
+    function CallAsn1GetHashCode(): Int32;
+
+  end;
+
+type
+  IDerObjectIdentifier = interface(IAsn1Object)
+
+    ['{8626051F-828D-419B-94E8-65CC0752CCA1}']
+
+    function GetID: String;
+    property ID: String read GetID;
+
+    procedure WriteField(const outputStream: TStream;
+      fieldValue: Int64); overload;
+    procedure WriteField(const outputStream: TStream;
+      const fieldValue: TBigInteger); overload;
+    procedure DoOutput(const bOut: TMemoryStream); overload;
+    function GetBody(): TCryptoLibByteArray;
+
+    function Branch(const branchID: String): IDerObjectIdentifier;
+
+    function &On(const stem: IDerObjectIdentifier): Boolean;
+
+    function ToString(): String;
+
+  end;
+
+type
+  IAsn1EncodableVector = interface(IInterface)
+    ['{A78E22EB-DB67-472E-A55F-CD710BCBDBFA}']
+
+    function GetCount: Int32;
+    function GetSelf(Index: Int32): IAsn1Encodable;
+
+    procedure Add(const objs: array of IAsn1Encodable);
+
+    procedure AddOptional(const objs: array of IAsn1Encodable);
+
+    property Self[Index: Int32]: IAsn1Encodable read GetSelf; default;
+
+    property Count: Int32 read GetCount;
+
+    function GetEnumerable: TCryptoLibGenericArray<IAsn1Encodable>;
+
+  end;
+
+type
+  IAsn1Generator = interface(IInterface)
+    ['{40EEDADB-1F59-46F7-9AB9-17C4761C6C66}']
+
+    function GetOut: TStream;
+
+    property &Out: TStream read GetOut;
+
+  end;
+
+type
+  IAsn1Null = interface(IAsn1Object)
+
+    ['{5BA79253-4596-4384-9B67-1F3589BD9D24}']
+
+    function ToString(): String;
+  end;
+
+type
+  IAsn1OctetStringParser = interface(IAsn1Convertible)
+    ['{8C24599C-B47F-4824-B110-2F10BE8B8377}']
+
+    function GetOctetStream(): TStream;
+  end;
+
+type
+  IAsn1OctetString = interface(IAsn1Object)
+    ['{7F7FE981-DD88-4076-8A99-F24DA1005475}']
+
+    function GetStr: TCryptoLibByteArray;
+    function GetParser: IAsn1OctetStringParser;
+
+    property Str: TCryptoLibByteArray read GetStr;
+    property Parser: IAsn1OctetStringParser read GetParser;
+
+    function GetOctetStream(): TStream;
+
+    function GetOctets(): TCryptoLibByteArray;
+
+    function ToString(): String;
+
+  end;
+
+type
+  IAsn1TaggedObject = interface(IAsn1Object)
+    ['{2EBA68BE-CEC6-4030-BB9C-E8310C9B7D5F}']
+
+    function GetTagNo: Int32;
+    function Getexplicitly: Boolean;
+    function Getobj: IAsn1Encodable;
+
+    property tagNo: Int32 read GetTagNo;
+    property explicitly: Boolean read Getexplicitly;
+    property obj: IAsn1Encodable read Getobj;
+
+    function isExplicit(): Boolean;
+
+    function IsEmpty(): Boolean;
+
+    function GetObject(): IAsn1Object;
+
+    function GetObjectParser(tag: Int32; isExplicit: Boolean): IAsn1Convertible;
+
+    function ToString(): String;
+
+  end;
+
+type
+  IAsn1TaggedObjectParser = interface(IAsn1Convertible)
+
+    ['{AB221E2D-A78A-46F7-85AE-B642D904B705}']
+
+    function GetTagNo: Int32;
+    property tagNo: Int32 read GetTagNo;
+    function GetObjectParser(tag: Int32; isExplicit: Boolean): IAsn1Convertible;
+  end;
+
+type
+  IDerOctetString = interface(IAsn1OctetString)
+
+    ['{0FFF8858-026C-49B5-A600-7F746FE6BCF7}']
+
+  end;
+
+type
+  IAsn1SequenceParser = interface(IAsn1Convertible)
+
+    ['{FAB73E91-6375-450C-938A-5C862D186857}']
+
+    function ReadObject(): IAsn1Convertible;
+  end;
+
+type
+  IAsn1Sequence = interface(IAsn1Object)
+
+    ['{37A263B7-6724-422B-9B4C-08EBA272045F}']
+
+    function GetCount: Int32;
+    function GetParser: IAsn1SequenceParser;
+    function GetSelf(Index: Integer): IAsn1Encodable;
+    function GetCurrent(const e: IAsn1Encodable): IAsn1Encodable;
+
+    procedure AddObject(const obj: IAsn1Encodable);
+
+    function ToString(): String;
+
+    function GetEnumerable: TCryptoLibGenericArray<IAsn1Encodable>;
+
+    property Self[Index: Int32]: IAsn1Encodable read GetSelf; default;
+
+    property Parser: IAsn1SequenceParser read GetParser;
+    property Count: Int32 read GetCount;
+
+  end;
+
+type
+  IAsn1SequenceParserImpl = interface(IAsn1SequenceParser)
+
+    ['{B986EDD8-A7F3-4E9C-9D5B-2FF9120D9A91}']
+
+  end;
+
+type
+  IBerOctetString = interface(IDerOctetString)
+
+    ['{B9D96DA7-623C-491C-9304-7B67A6DBCFA6}']
+
+    function GenerateOcts(): TList<IDerOctetString>;
+
+    /// <summary>
+    /// return the DER octets that make up this string.
+    /// </summary>
+
+    function GetEnumerable: TCryptoLibGenericArray<IDerOctetString>;
+
+  end;
+
+type
+  IDerNull = interface(IAsn1Null)
+
+    ['{0B4ABCBF-DF52-4934-8A43-218D948E1841}']
+
+  end;
+
+type
+  IDerTaggedObject = interface(IAsn1TaggedObject)
+
+    ['{CC77CFAB-8FCF-43E6-8FE4-95EFD99B9731}']
+
+  end;
+
+type
+  IBerTaggedObject = interface(IDerTaggedObject)
+
+    ['{DD6A102D-70DC-4428-8199-62D88276BDBA}']
+
+  end;
+
+type
+  IDerSequence = interface(IAsn1Sequence)
+
+    ['{ED1E13E2-6604-4FDE-BDB2-862704A9C90C}']
+
+  end;
+
+type
+  IBerSequence = interface(IDerSequence)
+
+    ['{B78E91BF-DB39-4033-8A7A-F0D024C5322A}']
+
+  end;
+
+type
+  IAsn1SetParser = interface(IAsn1Convertible)
+
+    ['{79248C62-59F2-4ACF-B62B-826FCCCF2AE1}']
+
+    function ReadObject(): IAsn1Convertible;
+  end;
+
+type
+  IAsn1Set = interface(IAsn1Object)
+    ['{0BA9633A-73D2-4F5E-A1C0-0FCF2623847C}']
+
+    function GetCount: Int32;
+    function GetParser: IAsn1SetParser;
+    function GetSelf(Index: Integer): IAsn1Encodable;
+    function GetCurrent(const e: IAsn1Encodable): IAsn1Encodable;
+
+    function ToString(): String;
+
+    function ToArray(): TCryptoLibGenericArray<IAsn1Encodable>;
+
+    function GetEnumerable: TCryptoLibGenericArray<IAsn1Encodable>;
+
+    property Self[Index: Int32]: IAsn1Encodable read GetSelf; default;
+
+    property Parser: IAsn1SetParser read GetParser;
+    property Count: Int32 read GetCount;
+
+  end;
+
+type
+  IAsn1SetParserImpl = interface(IAsn1SetParser)
+
+    ['{23EAFC37-244E-42D7-89A8-1740B12656C1}']
+
+  end;
+
+type
+
+  IDerSet = interface(IAsn1Set)
+
+    ['{592C8E57-5B00-4927-AD34-EA4481D436BE}']
+
+  end;
+
+type
+  /// **
+  // * Marker interface for CHOICE objects - if you implement this in a roll-your-own
+  // * object, any attempt to tag the object implicitly will convert the tag to an
+  // * explicit one as the encoding rules require.
+  // * <p>
+  // * If you use this interface your class should also implement the GetInstance
+  // * pattern which takes a tag object and the tagging mode used.
+  // * </p>
+  // */
+  IAsn1Choice = interface(IInterface)
+    // marker interface
+    ['{9C12BE01-9579-48F2-A5B0-4FA5DD807B32}']
+  end;
+
+type
+  IAsn1StreamParser = interface(IInterface)
+    ['{D6FF970C-F5B0-4B62-8585-F5F499A18597}']
+
+    procedure Set00Check(enabled: Boolean);
+
+    function ReadIndef(tagValue: Int32): IAsn1Convertible;
+    function ReadImplicit(constructed: Boolean; tag: Int32): IAsn1Convertible;
+
+    function ReadTaggedObject(constructed: Boolean; tag: Int32): IAsn1Object;
+
+    function ReadObject(): IAsn1Convertible;
+
+    function ReadVector(): IAsn1EncodableVector;
+
+  end;
+
+type
+  IDerSetParser = interface(IAsn1SetParser)
+
+    ['{C9F565EE-4324-4F67-8DC9-E2A305AAD715}']
+
+  end;
+
+type
+  IDerSequenceParser = interface(IAsn1SequenceParser)
+
+    ['{C6B7A49D-C2CC-43A1-9820-7B36AD4FD3B6}']
+
+  end;
+
+type
+  IDerApplicationSpecific = interface(IAsn1Object)
+    ['{87EE7482-8D8B-4593-85B7-B5B286B43195}']
+
+    function GetApplicationTag: Int32;
+    function GetLengthOfHeader(const data: TCryptoLibByteArray): Int32;
+
+    function isConstructed(): Boolean;
+    function GetContents(): TCryptoLibByteArray;
+
+    function GetObject(): IAsn1Object; overload;
+
+    function GetObject(derTagNo: Int32): IAsn1Object; overload;
+
+    property ApplicationTag: Int32 read GetApplicationTag;
+
+  end;
+
+type
+  IBerApplicationSpecific = interface(IDerApplicationSpecific)
+    ['{C7414497-904B-4CE8-8B5F-E252739F5B5C}']
+  end;
+
+type
+  IBerOctetStringParser = interface(IAsn1OctetStringParser)
+    ['{27698DDF-3584-45F6-8B6D-0AD85AA63F10}']
+
+  end;
+
+type
+  IAsn1ApplicationSpecificParser = interface(IAsn1Convertible)
+
+    ['{25C8291B-BE36-4916-8BD0-1F8629ACEE7F}']
+
+    function ReadObject(): IAsn1Convertible;
+  end;
+
+type
+  IBerApplicationSpecificParser = interface(IAsn1ApplicationSpecificParser)
+
+    ['{60363C2D-CE20-467C-BC1D-38DFCFDFDFFA}']
+
+  end;
+
+type
+  IDerStringBase = interface(IAsn1Object)
+    ['{710E6D65-28DE-4BFE-9C62-3CFC0E909DD9}']
+
+    function GetString(): String;
+    function ToString(): String;
+  end;
+
+type
+  IDerBitString = interface(IDerStringBase)
+
+    ['{2EBCCC24-BF14-4EB1-BADA-C521439682BE}']
+
+    function GetmData: TCryptoLibByteArray;
+    property mData: TCryptoLibByteArray read GetmData;
+
+    function GetmPadBits: Int32;
+    property mPadBits: Int32 read GetmPadBits;
+
+    function GetOctets(): TCryptoLibByteArray;
+
+    function GetBytes(): TCryptoLibByteArray;
+
+    function GetInt32Value: Int32;
+    property Int32Value: Int32 read GetInt32Value;
+
+  end;
+
+type
+
+  /// <summary>
+  /// basic interface for Der string objects.
+  /// </summary>
+  IAsn1String = interface(IInterface)
+    ['{D86B1260-C45A-42BE-A5B6-33F4C2DE4DF4}']
+
+    function GetString(): String;
+  end;
+
+type
+  IBerBitString = interface(IDerBitString)
+
+    ['{6AB7DC7E-CD61-4D34-AE15-99E036688C77}']
+
+  end;
+
+type
+  IBerGenerator = interface(IAsn1Generator)
+    ['{9ED01B6B-EF67-46B2-8EF6-023CA3967345}']
+
+    procedure WriteHdr(tag: Int32);
+
+    procedure WriteBerHeader(tag: Int32);
+    procedure WriteBerBody(contentStream: TStream);
+    procedure WriteBerEnd();
+
+    procedure AddObject(const obj: IAsn1Encodable);
+    function GetRawOutputStream(): TStream;
+    procedure Close();
+
+  end;
+
+type
+  IBerNull = interface(IDerNull)
+    ['{16AF74E8-26E1-466D-BE93-38A06853DA98}']
+
+  end;
+
+type
+  IBerSequenceGenerator = interface(IBerGenerator)
+    ['{D3EC8215-D932-40B3-B3C3-462C1558B88A}']
+
+  end;
+
+type
+  IBerSequenceParser = interface(IAsn1SequenceParser)
+
+    ['{9383F917-78CD-418C-B9B5-7972B907E8BE}']
+
+  end;
+
+type
+  IBerSet = interface(IDerSet)
+    ['{FD8838BB-8905-409A-AB93-136EEC6A05E4}']
+
+  end;
+
+type
+  IBerSetParser = interface(IAsn1SetParser)
+
+    ['{258A83DA-0C41-444D-9111-9CF7EAAEA54A}']
+
+  end;
+
+type
+  IBerTaggedObjectParser = interface(IAsn1TaggedObjectParser)
+
+    ['{F1E974C6-6C98-448D-945B-077E63ACB66F}']
+
+    function GetIsConstructed: Boolean;
+
+    property isConstructed: Boolean read GetIsConstructed;
+
+  end;
+
+type
+  IDerBmpString = interface(IDerStringBase)
+
+    ['{D100D2D5-5B11-47DC-99A4-A968EF31F5FC}']
+
+    function GetStr: String;
+
+    property Str: String read GetStr;
+
+  end;
+
+type
+  IDerBoolean = interface(IAsn1Object)
+    ['{1AD2B466-E3D6-4CD4-A66B-660FB63F2FFE}']
+
+    function GetIsTrue: Boolean;
+
+    function ToString(): String;
+
+    property IsTrue: Boolean read GetIsTrue;
+
+  end;
+
+type
+  IDerEnumerated = interface(IAsn1Object)
+    ['{876D2762-5B34-4CC0-815A-D10DF1318D12}']
+
+    function GetValue: TBigInteger;
+    function GetBytes: TCryptoLibByteArray;
+
+    property Value: TBigInteger read GetValue;
+    property bytes: TCryptoLibByteArray read GetBytes;
+
+  end;
+
+type
+  IDerGraphicString = interface(IDerStringBase)
+    ['{582F6117-BFE1-47A2-BFD5-DC5C83E43985}']
+
+    function GetmString: TCryptoLibByteArray;
+
+    property mString: TCryptoLibByteArray read GetmString;
+
+    function GetOctets(): TCryptoLibByteArray;
+
+  end;
+
+type
+
+  IDerInteger = interface(IAsn1Object)
+    ['{B968152A-5A16-4C1D-95E1-3B5F416D2C75}']
+
+    function GetBytes: TCryptoLibByteArray;
+    function GetPositiveValue: TBigInteger;
+    function GetValue: TBigInteger;
+
+    function ToString(): String;
+
+    property Value: TBigInteger read GetValue;
+    property PositiveValue: TBigInteger read GetPositiveValue;
+    property bytes: TCryptoLibByteArray read GetBytes;
+
+  end;
+
+type
+  IDerExternal = interface(IAsn1Object)
+
+    ['{9AC333C2-0F64-4A5F-BE0A-EBCC2A4E2A00}']
+
+    function GetDataValueDescriptor: IAsn1Object;
+    function GetDirectReference: IDerObjectIdentifier;
+
+    function GetEncoding: Int32;
+    function GetExternalContent: IAsn1Object;
+    function GetIndirectReference: IDerInteger;
+    procedure SetDataValueDescriptor(const Value: IAsn1Object);
+    procedure SetDirectReference(const Value: IDerObjectIdentifier);
+    procedure SetEncoding(const Value: Int32);
+    procedure SetExternalContent(const Value: IAsn1Object);
+    procedure SetIndirectReference(const Value: IDerInteger);
+
+    property DataValueDescriptor: IAsn1Object read GetDataValueDescriptor
+      write SetDataValueDescriptor;
+
+    property DirectReference: IDerObjectIdentifier read GetDirectReference
+      write SetDirectReference;
+
+    property encoding: Int32 read GetEncoding write SetEncoding;
+
+    property ExternalContent: IAsn1Object read GetExternalContent
+      write SetExternalContent;
+
+    property IndirectReference: IDerInteger read GetIndirectReference
+      write SetIndirectReference;
+
+  end;
+
+type
+  IDerExternalParser = interface(IAsn1Encodable)
+    ['{6BF2AB32-0307-4E49-BC4C-844ADCD884E0}']
+
+    function ReadObject(): IAsn1Convertible;
+  end;
+
+type
+  IDerOctetStringParser = interface(IAsn1OctetStringParser)
+    ['{49664C03-CD81-423F-A93D-025D3116B066}']
+
+  end;
+
+type
+  IDerGeneralString = interface(IDerStringBase)
+
+    ['{E1C5097C-2FA1-4236-85D9-30784E6AA46D}']
+
+    function GetStr: String;
+
+    property Str: String read GetStr;
+
+    function GetOctets(): TCryptoLibByteArray;
+
+  end;
+
+type
+  IDerGenerator = interface(IAsn1Generator)
+    ['{2A2A6C30-6CF7-41D2-9421-923A4A82E52B}']
+
+    procedure WriteDerEncoded(tag: Int32; const bytes: TCryptoLibByteArray);
+
+  end;
+
+type
+  IDerIA5String = interface(IDerStringBase)
+
+    ['{F7BAC857-74F7-4660-95E1-F849B5D77F6C}']
+
+    function GetStr: String;
+
+    property Str: String read GetStr;
+
+    function GetOctets(): TCryptoLibByteArray;
+
+  end;
+
+type
+  IDerNumericString = interface(IDerStringBase)
+
+    ['{58BB62CA-16C5-4696-AC0B-E83628182740}']
+
+    function GetStr: String;
+
+    property Str: String read GetStr;
+
+    function GetOctets(): TCryptoLibByteArray;
+
+  end;
+
+type
+  IDerPrintableString = interface(IDerStringBase)
+
+    ['{119C220A-2672-48E3-A24A-11128B5F599B}']
+
+    function GetStr: String;
+
+    property Str: String read GetStr;
+
+    function GetOctets(): TCryptoLibByteArray;
+
+  end;
+
+type
+  IDerSequenceGenerator = interface(IDerGenerator)
+    ['{1E0E4FD7-84CA-4D02-AB9E-5AF8461270DE}']
+    procedure AddObject(const obj: IAsn1Encodable);
+    function GetRawOutputStream(): TStream;
+    procedure Close();
+  end;
+
+type
+  IDerT61String = interface(IDerStringBase)
+
+    ['{A3B8C316-F349-4A5A-96CB-4655581A0308}']
+
+    function GetStr: String;
+
+    property Str: String read GetStr;
+
+    function GetOctets(): TCryptoLibByteArray;
+
+  end;
+
+type
+  IDerUniversalString = interface(IDerStringBase)
+
+    ['{60EC8C9A-B672-44E4-9C5B-B4022D937002}']
+
+    function GetStr: TCryptoLibByteArray;
+
+    property Str: TCryptoLibByteArray read GetStr;
+
+    function GetOctets(): TCryptoLibByteArray;
+
+  end;
+
+type
+  IDerUtf8String = interface(IDerStringBase)
+
+    ['{C4ACD432-807D-4A27-B3FC-0694000EB995}']
+
+    function GetStr: String;
+
+    property Str: String read GetStr;
+
+  end;
+
+type
+  IDerVideotexString = interface(IDerStringBase)
+    ['{9484AEC5-5667-4C14-8441-4447B0B29F1B}']
+
+    function GetmString: TCryptoLibByteArray;
+
+    property mString: TCryptoLibByteArray read GetmString;
+
+    function GetOctets(): TCryptoLibByteArray;
+
+  end;
+
+type
+  IDerVisibleString = interface(IDerStringBase)
+
+    ['{0540C649-B50B-45DA-9FC9-1248BD3F73F1}']
+
+    function GetStr: String;
+
+    property Str: String read GetStr;
+
+    function GetOctets(): TCryptoLibByteArray;
+
+  end;
+
+implementation
+
+end.

+ 48 - 0
src/libraries/cryptolib4pascal/ClpIAsymmetricCipherKeyPair.pas

@@ -0,0 +1,48 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIAsymmetricCipherKeyPair;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIAsymmetricKeyParameter;
+
+type
+  IAsymmetricCipherKeyPair = interface(IInterface)
+    ['{66AD23CF-CE95-49C3-B9F0-83153174E9D7}']
+
+    function GetPrivate: IAsymmetricKeyParameter;
+    function GetPublic: IAsymmetricKeyParameter;
+
+    /// <summary>
+    /// return the public key parameters.
+    /// </summary>
+    property &Public: IAsymmetricKeyParameter read GetPublic;
+
+    /// <summary>
+    /// return the private key parameters.
+    /// </summary>
+    property &Private: IAsymmetricKeyParameter read GetPrivate;
+
+  end;
+
+implementation
+
+end.

+ 50 - 0
src/libraries/cryptolib4pascal/ClpIAsymmetricCipherKeyPairGenerator.pas

@@ -0,0 +1,50 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIAsymmetricCipherKeyPairGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIKeyGenerationParameters,
+  ClpIAsymmetricCipherKeyPair;
+
+type
+  IAsymmetricCipherKeyPairGenerator = interface(IInterface)
+    ['{BC73E9BF-24B2-4833-A8DA-B690FCC81A2F}']
+
+    // /**
+    // * intialise the key pair generator.
+    // *
+    // * @param the parameters the key pair is to be initialised with.
+    // */
+    procedure Init(const parameters: IKeyGenerationParameters);
+
+    // /**
+    // * return an AsymmetricCipherKeyPair containing the Generated keys.
+    // *
+    // * @return an AsymmetricCipherKeyPair containing the Generated keys.
+    // */
+    function GenerateKeyPair(): IAsymmetricCipherKeyPair;
+
+  end;
+
+implementation
+
+end.

+ 43 - 0
src/libraries/cryptolib4pascal/ClpIAsymmetricKeyParameter.pas

@@ -0,0 +1,43 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIAsymmetricKeyParameter;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpICipherParameters;
+
+type
+
+  IAsymmetricKeyParameter = interface(ICipherParameters)
+    ['{306A2860-9D12-46BF-9994-BCDCEF63214F}']
+
+    function GetIsPrivate: Boolean;
+    function GetPrivateKey: Boolean;
+    property IsPrivate: Boolean read GetIsPrivate;
+    property PrivateKey: Boolean read GetPrivateKey;
+    function Equals(const other: IAsymmetricKeyParameter): Boolean;
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+  end;
+
+implementation
+
+end.

+ 36 - 0
src/libraries/cryptolib4pascal/ClpIBaseKdfBytesGenerator.pas

@@ -0,0 +1,36 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIBaseKdfBytesGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDerivationFunction;
+
+type
+
+  IBaseKdfBytesGenerator = interface(IDerivationFunction)
+    ['{EFA36EF4-5403-4F47-A222-2F64820C9188}']
+
+  end;
+
+implementation
+
+end.

+ 58 - 0
src/libraries/cryptolib4pascal/ClpIBasicAgreement.pas

@@ -0,0 +1,58 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIBasicAgreement;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpBigInteger,
+  ClpICipherParameters;
+
+type
+
+  /// <summary>
+  /// The basic interface that basic Diffie-Hellman implementations conforms
+  /// to.
+  /// </summary>
+  IBasicAgreement = interface(IInterface)
+
+    ['{4A36A62D-3E1F-49B4-A4CD-58348E5A837F}']
+
+    /// <summary>
+    /// initialise the agreement engine.
+    /// </summary>
+    procedure Init(const parameters: ICipherParameters);
+
+    /// <summary>
+    /// return the field size for the agreement algorithm in bytes.
+    /// </summary>
+    function GetFieldSize(): Int32;
+
+    /// <summary>
+    /// given a public key from a given party calculate the next message
+    /// in the agreement sequence.
+    /// </summary>
+    function CalculateAgreement(const pubKey: ICipherParameters): TBigInteger;
+
+  end;
+
+implementation
+
+end.

+ 68 - 0
src/libraries/cryptolib4pascal/ClpIBlockCipher.pas

@@ -0,0 +1,68 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIBlockCipher;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpICipherParameters,
+  ClpCryptoLibTypes;
+
+type
+  /// <remarks>Base interface for a symmetric key block cipher.</remarks>
+  IBlockCipher = interface(IInterface)
+    ['{0D2145AA-9D1E-4955-A55E-645CC8DEBE6F}']
+
+    /// <summary>The name of the algorithm this cipher implements.</summary>
+    function GetAlgorithmName: String;
+    property AlgorithmName: String read GetAlgorithmName;
+
+    /// <summary>Initialise the cipher.</summary>
+    /// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
+    /// <param name="parameters">The key or other data required by the cipher.</param>
+    procedure Init(forEncryption: Boolean; const parameters: ICipherParameters);
+
+    /// <returns>The block size for this cipher, in bytes.</returns>
+    function GetBlockSize(): Int32;
+
+    /// <summary>Indicates whether this cipher can handle partial blocks.</summary>
+    function GetIsPartialBlockOkay: Boolean;
+    property IsPartialBlockOkay: Boolean read GetIsPartialBlockOkay;
+
+    /// <summary>Process a block.</summary>
+    /// <param name="inBuf">The input buffer.</param>
+    /// <param name="inOff">The offset into <paramref>inBuf</paramref> that the input block begins.</param>
+    /// <param name="outBuf">The output buffer.</param>
+    /// <param name="outOff">The offset into <paramref>outBuf</paramref> to write the output block.</param>
+    /// <exception cref="DataLengthException">If input block is wrong size, or outBuf too small.</exception>
+    /// <returns>The number of bytes processed and produced.</returns>
+    function ProcessBlock(const inBuf: TCryptoLibByteArray; inOff: Int32;
+      const outBuf: TCryptoLibByteArray; outOff: Int32): Int32;
+
+    /// <summary>
+    /// Reset the cipher to the same state as it was after the last init (if there was one).
+    /// </summary>
+    procedure Reset();
+
+  end;
+
+implementation
+
+end.

+ 89 - 0
src/libraries/cryptolib4pascal/ClpIBlockCipherModes.pas

@@ -0,0 +1,89 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIBlockCipherModes;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIBlockCipher;
+
+type
+  ICbcBlockCipher = interface(IBlockCipher)
+
+    ['{A95FD7C2-02FE-4600-B496-B7A757737BFF}']
+
+    /// <summary>
+    /// return the underlying block cipher that we are wrapping.
+    /// </summary>
+    /// <returns>
+    /// return the underlying block cipher that we are wrapping.
+    /// </returns>
+    function GetUnderlyingCipher(): IBlockCipher;
+
+  end;
+
+type
+  ICfbBlockCipher = interface(IBlockCipher)
+
+    ['{A58FD8F1-EECF-402F-9007-4E884FF7D325}']
+
+    /// <summary>
+    /// return the underlying block cipher that we are wrapping.
+    /// </summary>
+    /// <returns>
+    /// return the underlying block cipher that we are wrapping.
+    /// </returns>
+    function GetUnderlyingCipher(): IBlockCipher;
+
+  end;
+
+type
+  IOfbBlockCipher = interface(IBlockCipher)
+
+    ['{17D4977F-C9D8-466D-8E46-7E23A03471FC}']
+
+    /// <summary>
+    /// return the underlying block cipher that we are wrapping.
+    /// </summary>
+    /// <returns>
+    /// return the underlying block cipher that we are wrapping.
+    /// </returns>
+    function GetUnderlyingCipher(): IBlockCipher;
+
+  end;
+
+type
+  ISicBlockCipher = interface(IBlockCipher)
+
+    ['{85CC4B84-8E49-40E1-B2F9-6C271C1FB5E7}']
+
+    /// <summary>
+    /// return the underlying block cipher that we are wrapping.
+    /// </summary>
+    /// <returns>
+    /// return the underlying block cipher that we are wrapping.
+    /// </returns>
+    function GetUnderlyingCipher(): IBlockCipher;
+
+  end;
+
+implementation
+
+end.

+ 84 - 0
src/libraries/cryptolib4pascal/ClpIBlockCipherPadding.pas

@@ -0,0 +1,84 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIBlockCipherPadding;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpISecureRandom,
+  ClpCryptoLibTypes;
+
+type
+  /// <remarks>Block cipher padders are expected to conform to this interface.</remarks>
+  IBlockCipherPadding = interface(IInterface)
+    ['{BF2886A5-1020-4270-B8E5-3BA9FDB9DA56}']
+
+    /// <summary>
+    /// Initialise the padder.
+    /// </summary>
+    /// <param name="random">
+    /// param parameters, if any required.
+    /// </param>
+    procedure Init(const random: ISecureRandom);
+
+    /// <returns>
+    /// return the name of the algorithm the cipher implements.
+    /// </returns>
+    function GetPaddingName: String;
+
+    /// <summary>
+    /// Return the name of the algorithm the cipher implements.
+    /// </summary>
+    property PaddingName: String read GetPaddingName;
+
+    /// <summary>
+    /// add the pad bytes to the passed in block, returning the number of
+    /// bytes added.
+    /// </summary>
+    /// <param name="input">
+    /// input block to pad
+    /// </param>
+    /// <param name="inOff">
+    /// offset to start the padding from in the block
+    /// </param>
+    /// <returns>
+    /// returns number of bytes added
+    /// </returns>
+    function AddPadding(const input: TCryptoLibByteArray; inOff: Int32): Int32;
+
+    /// <summary>
+    /// return the number of pad bytes present in the block.
+    /// </summary>
+    /// <param name="input">
+    /// block to count pad bytes in
+    /// </param>
+    /// <returns>
+    /// the number of pad bytes present in the block.
+    /// </returns>
+    /// <exception cref="EInvalidCipherTextCryptoLibException">
+    /// if the padding is badly formed or invalid.
+    /// </exception>
+    function PadCount(const input: TCryptoLibByteArray): Int32;
+
+  end;
+
+implementation
+
+end.

+ 36 - 0
src/libraries/cryptolib4pascal/ClpIBlowfishEngine.pas

@@ -0,0 +1,36 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIBlowfishEngine;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIBlockCipher;
+
+type
+
+  IBlowfishEngine = interface(IBlockCipher)
+    ['{005CFC45-DEB3-4AC1-896D-1370AF09F3C5}']
+
+  end;
+
+implementation
+
+end.

+ 36 - 0
src/libraries/cryptolib4pascal/ClpIBufferedBlockCipher.pas

@@ -0,0 +1,36 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIBufferedBlockCipher;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIBufferedCipherBase;
+
+type
+  IBufferedBlockCipher = interface(IBufferedCipherBase)
+
+    ['{A71190C5-154E-4C2B-B6B2-BC4460B201B5}']
+
+  end;
+
+implementation
+
+end.

+ 107 - 0
src/libraries/cryptolib4pascal/ClpIBufferedCipher.pas

@@ -0,0 +1,107 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIBufferedCipher;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpICipherParameters,
+  ClpCryptoLibTypes;
+
+type
+  TBufferedCipherProgressEvent = procedure(AProcessed, ATotal: Int64);
+
+type
+  /// <remarks>Block cipher engines are expected to conform to this interface.</remarks>
+  IBufferedCipher = interface(IInterface)
+    ['{44FE589C-EDAE-4FDC-90AE-90C0935B4BC7}']
+    /// <summary>The name of the algorithm this cipher implements.</summary>
+    function GetAlgorithmName: String;
+    property AlgorithmName: String read GetAlgorithmName;
+
+    function GetBufferSize: Int32;
+    procedure SetBufferSize(value: Int32);
+    /// <summary>
+    /// property for determining the buffer size to use for stream based
+    /// encryption/decryption.
+    /// </summary>
+    property BufferSize: Int32 read GetBufferSize write SetBufferSize;
+
+    function GetOnProgress: TBufferedCipherProgressEvent;
+    procedure SetOnProgress(const value: TBufferedCipherProgressEvent);
+
+    property OnProgress: TBufferedCipherProgressEvent read GetOnProgress
+      write SetOnProgress;
+
+    /// <summary>Initialise the cipher.</summary>
+    /// <param name="forEncryption">If true the cipher is initialised for encryption,
+    /// if false for decryption.</param>
+    /// <param name="parameters">The key and other data required by the cipher.</param>
+    procedure Init(forEncryption: Boolean; const parameters: ICipherParameters);
+
+    function GetBlockSize(): Int32;
+
+    function GetOutputSize(inputLen: Int32): Int32;
+
+    function GetUpdateOutputSize(inputLen: Int32): Int32;
+
+    procedure ProcessStream(const inputStream, outputStream: TStream;
+      Length: Int64); overload;
+
+    procedure ProcessStream(const inputStream: TStream; inOff: Int64;
+      const outputStream: TStream; outOff: Int64; Length: Int64); overload;
+
+    function ProcessByte(input: Byte): TCryptoLibByteArray; overload;
+    function ProcessByte(input: Byte; const output: TCryptoLibByteArray;
+      outOff: Int32): Int32; overload;
+
+    function ProcessBytes(const input: TCryptoLibByteArray)
+      : TCryptoLibByteArray; overload;
+    function ProcessBytes(const input: TCryptoLibByteArray;
+      inOff, Length: Int32): TCryptoLibByteArray; overload;
+    function ProcessBytes(const input, output: TCryptoLibByteArray;
+      outOff: Int32): Int32; overload;
+    function ProcessBytes(const input: TCryptoLibByteArray;
+      inOff, Length: Int32; const output: TCryptoLibByteArray; outOff: Int32)
+      : Int32; overload;
+
+    function DoFinal(): TCryptoLibByteArray; overload;
+    function DoFinal(const input: TCryptoLibByteArray)
+      : TCryptoLibByteArray; overload;
+    function DoFinal(const input: TCryptoLibByteArray; inOff, Length: Int32)
+      : TCryptoLibByteArray; overload;
+    function DoFinal(const output: TCryptoLibByteArray; outOff: Int32)
+      : Int32; overload;
+    function DoFinal(const input, output: TCryptoLibByteArray; outOff: Int32)
+      : Int32; overload;
+    function DoFinal(const input: TCryptoLibByteArray; inOff, Length: Int32;
+      const output: TCryptoLibByteArray; outOff: Int32): Int32; overload;
+
+    /// <summary>
+    /// Reset the cipher. After resetting the cipher is in the same state
+    /// as it was after the last init (if there was one).
+    /// </summary>
+    procedure Reset();
+  end;
+
+implementation
+
+end.

+ 35 - 0
src/libraries/cryptolib4pascal/ClpIBufferedCipherBase.pas

@@ -0,0 +1,35 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIBufferedCipherBase;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIBufferedCipher;
+
+type
+
+  IBufferedCipherBase = interface(IBufferedCipher)
+    ['{3CA69950-0A10-4227-B536-BFB3151076F9}']
+  end;
+
+implementation
+
+end.

+ 57 - 0
src/libraries/cryptolib4pascal/ClpICipherKeyGenerator.pas

@@ -0,0 +1,57 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpICipherKeyGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIKeyGenerationParameters,
+  ClpCryptoLibTypes;
+
+type
+
+  ICipherKeyGenerator = interface(IInterface)
+    ['{084FE16F-7AEA-42C0-92BB-6CEC7923DE6F}']
+
+    /// <summary>
+    /// initialise the key generator.
+    /// </summary>
+    /// <param name="parameters">
+    /// the parameters to be used for key generation
+    /// </param>
+    procedure Init(const parameters: IKeyGenerationParameters);
+
+    /// <summary>
+    /// Generate a secret key.
+    /// </summary>
+    /// <returns>
+    /// a byte array containing the key value.
+    /// </returns>
+    function GenerateKey: TCryptoLibByteArray;
+
+    function GetdefaultStrength: Int32;
+
+    property defaultStrength: Int32 read GetdefaultStrength;
+
+  end;
+
+implementation
+
+end.

+ 36 - 0
src/libraries/cryptolib4pascal/ClpICipherParameters.pas

@@ -0,0 +1,36 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpICipherParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+type
+
+  /// <summary>
+  /// all parameter classes implement this.
+  /// </summary>
+  ICipherParameters = interface(IInterface)
+    ['{F0418659-D74C-4B6A-85C0-A76E7CD1A9A2}']
+
+  end;
+
+implementation
+
+end.

+ 35 - 0
src/libraries/cryptolib4pascal/ClpICryptoApiRandomGenerator.pas

@@ -0,0 +1,35 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpICryptoApiRandomGenerator;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIRandomGenerator;
+
+type
+
+  ICryptoApiRandomGenerator = interface(IRandomGenerator)
+    ['{9602F758-A63D-4F60-94AF-26572B85BC81}']
+  end;
+
+implementation
+
+end.

+ 55 - 0
src/libraries/cryptolib4pascal/ClpIDerivationFunction.pas

@@ -0,0 +1,55 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIDerivationFunction;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDigest,
+  ClpIDerivationParameters,
+  ClpCryptoLibTypes;
+
+type
+
+  /// <summary>
+  /// base interface for general purpose byte derivation functions.
+  /// </summary>
+  IDerivationFunction = interface(IInterface)
+    ['{A9DA624C-A58E-4588-9EA0-81BA5B13E47E}']
+
+    procedure Init(const parameters: IDerivationParameters);
+
+    function GetDigest(): IDigest;
+
+    /// <value>
+    /// return the message digest used as the basis for the function
+    /// </value>
+    property Digest: IDigest read GetDigest;
+
+    /// <exception cref="EDataLengthCryptoLibException" />
+    /// <exception cref="EArgumentCryptoLibException" />
+    function GenerateBytes(const output: TCryptoLibByteArray;
+      outOff, length: Int32): Int32;
+
+  end;
+
+implementation
+
+end.

+ 36 - 0
src/libraries/cryptolib4pascal/ClpIDerivationParameters.pas

@@ -0,0 +1,36 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIDerivationParameters;
+
+{$I CryptoLib.inc}
+
+interface
+
+type
+
+  /// <summary>
+  /// Parameters for key/byte stream derivation classes
+  /// </summary>
+  IDerivationParameters = interface(IInterface)
+    ['{4F245EE6-734C-4D45-ADB5-F719F4098BB5}']
+
+  end;
+
+implementation
+
+end.

+ 97 - 0
src/libraries/cryptolib4pascal/ClpIDigest.pas

@@ -0,0 +1,97 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIDigest;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  HlpIHash,
+  ClpCryptoLibTypes;
+
+type
+  // interface that a message digest conforms to.
+  IDigest = interface(IInterface)
+    ['{4AF1A541-DABE-4F89-8E9E-26DB61097330}']
+
+    function GetAlgorithmName: string;
+
+    /// <summary>
+    /// the algorithm name
+    /// </summary>
+    property AlgorithmName: String read GetAlgorithmName;
+
+    /// <summary>
+    /// Gets the Underlying <b>IHash</b> Instance
+    /// </summary>
+    function GetUnderlyingIHash: IHash;
+
+    /// <summary>
+    /// the size, in bytes, of the digest produced by this message digest.
+    /// </summary>
+    function GetDigestSize(): Int32;
+
+    /// <summary>
+    /// the size, in bytes, of the internal buffer used by this digest.
+    /// </summary>
+    function GetByteLength(): Int32;
+
+    /// <summary>
+    /// update the message digest with a single byte.
+    /// </summary>
+    procedure Update(input: Byte);
+
+    /// <summary>
+    /// update the message digest with a block of bytes.
+    /// </summary>
+    /// <param name="input">
+    /// the byte array containing the data.
+    /// </param>
+    /// <param name="inOff">
+    /// the offset into the byte array where the data starts.
+    /// </param>
+    /// <param name="len">
+    /// the length of the data.
+    /// </param>
+    procedure BlockUpdate(const input: TCryptoLibByteArray; inOff, len: Int32);
+
+    /// <summary>
+    /// Close the digest, producing the final digest value. The doFinal call
+    /// leaves the digest reset.
+    /// </summary>
+    /// <param name="output">
+    /// the array the digest is to be copied into.
+    /// </param>
+    /// <param name="outOff">
+    /// the offset into the out array the digest is to start at.
+    /// </param>
+    function DoFinal(const output: TCryptoLibByteArray; outOff: Int32)
+      : Int32; overload;
+    function DoFinal: TCryptoLibByteArray; overload;
+
+    /// <summary>
+    /// Resets the digest back to it's initial state.
+    /// </summary>
+    procedure Reset();
+
+  end;
+
+implementation
+
+end.

Some files were not shown because too many files changed in this diff