Browse Source

add CTS Block Cipher Modes of Operation

Ugochukwu Mmaduekwe 6 years ago
parent
commit
3daf49f8dd

+ 2 - 1
CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.TestInsight.dpr

@@ -344,7 +344,8 @@ uses
   Salsa20Tests in '..\src\Crypto\Salsa20Tests.pas',
   Salsa20Tests in '..\src\Crypto\Salsa20Tests.pas',
   ChaChaTests in '..\src\Crypto\ChaChaTests.pas',
   ChaChaTests in '..\src\Crypto\ChaChaTests.pas',
   XSalsa20Tests in '..\src\Crypto\XSalsa20Tests.pas',
   XSalsa20Tests in '..\src\Crypto\XSalsa20Tests.pas',
-  StreamCipherResetTests in '..\src\Crypto\StreamCipherResetTests.pas';
+  StreamCipherResetTests in '..\src\Crypto\StreamCipherResetTests.pas',
+  CTSTests in '..\src\Crypto\CTSTests.pas';
 
 
 begin
 begin
 
 

+ 2 - 1
CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr

@@ -347,7 +347,8 @@ uses
   Salsa20Tests in '..\src\Crypto\Salsa20Tests.pas',
   Salsa20Tests in '..\src\Crypto\Salsa20Tests.pas',
   ChaChaTests in '..\src\Crypto\ChaChaTests.pas',
   ChaChaTests in '..\src\Crypto\ChaChaTests.pas',
   XSalsa20Tests in '..\src\Crypto\XSalsa20Tests.pas',
   XSalsa20Tests in '..\src\Crypto\XSalsa20Tests.pas',
-  StreamCipherResetTests in '..\src\Crypto\StreamCipherResetTests.pas';
+  StreamCipherResetTests in '..\src\Crypto\StreamCipherResetTests.pas',
+  CTSTests in '..\src\Crypto\CTSTests.pas';
 
 
 begin
 begin
 
 

+ 52 - 6
CryptoLib.Tests/FreePascal.Tests/CryptoLib.Tests.lpi

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
 <CONFIG>
   <ProjectOptions>
   <ProjectOptions>
-    <Version Value="10"/>
+    <Version Value="11"/>
     <PathDelim Value="\"/>
     <PathDelim Value="\"/>
     <General>
     <General>
       <SessionStorage Value="InProjectDir"/>
       <SessionStorage Value="InProjectDir"/>
@@ -10,16 +10,58 @@
       <ResourceType Value="res"/>
       <ResourceType Value="res"/>
       <UseXPManifest Value="True"/>
       <UseXPManifest Value="True"/>
     </General>
     </General>
-    <BuildModes Count="1">
+    <BuildModes Count="2">
       <Item1 Name="Default" Default="True"/>
       <Item1 Name="Default" Default="True"/>
+      <Item2 Name="Debug">
+        <CompilerOptions>
+          <Version Value="11"/>
+          <PathDelim Value="\"/>
+          <Target>
+            <Filename Value=".\bin\CryptoLib"/>
+          </Target>
+          <SearchPaths>
+            <IncludeFiles Value="$(ProjOutDir)"/>
+            <OtherUnitFiles Value="..\src\Asn1;..\src\Math;..\src\Math\EC\Custom\Sec;..\src\Others;..\src\Security;..\src\Utils;..\src\Crypto;..\src\Math\EC"/>
+            <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
+          </SearchPaths>
+          <Parsing>
+            <SyntaxOptions>
+              <IncludeAssertionCode Value="True"/>
+            </SyntaxOptions>
+          </Parsing>
+          <CodeGeneration>
+            <Checks>
+              <IOChecks Value="True"/>
+              <RangeChecks Value="True"/>
+              <OverflowChecks Value="True"/>
+              <StackChecks Value="True"/>
+            </Checks>
+            <VerifyObjMethodCallValidity Value="True"/>
+          </CodeGeneration>
+          <Linking>
+            <Debugging>
+              <DebugInfoType Value="dsDwarf2Set"/>
+              <UseHeaptrc Value="True"/>
+              <TrashVariables Value="True"/>
+              <UseExternalDbgSyms Value="True"/>
+            </Debugging>
+            <Options>
+              <Win32>
+                <GraphicApplication Value="True"/>
+              </Win32>
+            </Options>
+          </Linking>
+        </CompilerOptions>
+      </Item2>
     </BuildModes>
     </BuildModes>
     <PublishOptions>
     <PublishOptions>
       <Version Value="2"/>
       <Version Value="2"/>
     </PublishOptions>
     </PublishOptions>
     <RunParams>
     <RunParams>
-      <local>
-        <FormatVersion Value="1"/>
-      </local>
+      <FormatVersion Value="2"/>
+      <Modes Count="1">
+        <Mode0 Name="default"/>
+      </Modes>
     </RunParams>
     </RunParams>
     <RequiredPackages Count="4">
     <RequiredPackages Count="4">
       <Item1>
       <Item1>
@@ -35,7 +77,7 @@
         <PackageName Value="FCL"/>
         <PackageName Value="FCL"/>
       </Item4>
       </Item4>
     </RequiredPackages>
     </RequiredPackages>
-    <Units Count="54">
+    <Units Count="55">
       <Unit0>
       <Unit0>
         <Filename Value="CryptoLib.lpr"/>
         <Filename Value="CryptoLib.lpr"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
@@ -253,6 +295,10 @@
         <Filename Value="..\src\Crypto\ChaChaTests.pas"/>
         <Filename Value="..\src\Crypto\ChaChaTests.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
       </Unit53>
       </Unit53>
+      <Unit54>
+        <Filename Value="..\src\Crypto\CTSTests.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit54>
     </Units>
     </Units>
   </ProjectOptions>
   </ProjectOptions>
   <CompilerOptions>
   <CompilerOptions>

+ 1 - 0
CryptoLib.Tests/FreePascal.Tests/CryptoLib.lpr

@@ -57,6 +57,7 @@ uses
   XSalsa20Tests,
   XSalsa20Tests,
   ChaChaTests,
   ChaChaTests,
   StreamCipherResetTests,
   StreamCipherResetTests,
+  CTSTests,
   ClpFixedSecureRandom,
   ClpFixedSecureRandom,
   ClpIFixedSecureRandom;
   ClpIFixedSecureRandom;
 
 

+ 14 - 3
CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.Tests.lpi

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
 <CONFIG>
   <ProjectOptions>
   <ProjectOptions>
-    <Version Value="10"/>
+    <Version Value="11"/>
     <PathDelim Value="\"/>
     <PathDelim Value="\"/>
     <General>
     <General>
       <SessionStorage Value="InProjectDir"/>
       <SessionStorage Value="InProjectDir"/>
@@ -18,9 +18,16 @@
     </PublishOptions>
     </PublishOptions>
     <RunParams>
     <RunParams>
       <local>
       <local>
-        <FormatVersion Value="1"/>
         <CommandLineParams Value="--format=plain --all --progress"/>
         <CommandLineParams Value="--format=plain --all --progress"/>
       </local>
       </local>
+      <FormatVersion Value="2"/>
+      <Modes Count="1">
+        <Mode0 Name="default">
+          <local>
+            <CommandLineParams Value="--format=plain --all --progress"/>
+          </local>
+        </Mode0>
+      </Modes>
     </RunParams>
     </RunParams>
     <RequiredPackages Count="2">
     <RequiredPackages Count="2">
       <Item1>
       <Item1>
@@ -30,7 +37,7 @@
         <PackageName Value="FCL"/>
         <PackageName Value="FCL"/>
       </Item2>
       </Item2>
     </RequiredPackages>
     </RequiredPackages>
-    <Units Count="54">
+    <Units Count="55">
       <Unit0>
       <Unit0>
         <Filename Value="CryptoLibConsole.lpr"/>
         <Filename Value="CryptoLibConsole.lpr"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
@@ -247,6 +254,10 @@
         <Filename Value="..\src\Crypto\ChaChaTests.pas"/>
         <Filename Value="..\src\Crypto\ChaChaTests.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
       </Unit53>
       </Unit53>
+      <Unit54>
+        <Filename Value="..\src\Crypto\CTSTests.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit54>
     </Units>
     </Units>
   </ProjectOptions>
   </ProjectOptions>
   <CompilerOptions>
   <CompilerOptions>

+ 1 - 0
CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpr

@@ -55,6 +55,7 @@ uses
   XSalsa20Tests,
   XSalsa20Tests,
   ChaChaTests,
   ChaChaTests,
   StreamCipherResetTests,
   StreamCipherResetTests,
+  CTSTests,
   ClpFixedSecureRandom,
   ClpFixedSecureRandom,
   ClpIFixedSecureRandom;
   ClpIFixedSecureRandom;
 
 

+ 194 - 0
CryptoLib.Tests/src/Crypto/CTSTests.pas

@@ -0,0 +1,194 @@
+{ *********************************************************************************** }
+{ *                              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 CTSTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpAesEngine,
+  ClpIAesEngine,
+  ClpIKeyParameter,
+  ClpKeyParameter,
+  ClpParametersWithIV,
+  ClpIParametersWithIV,
+  // ClpIBufferedCipher,
+  ClpIBufferedBlockCipher,
+  // ClpICipherKeyGenerator,
+  ClpICipherParameters,
+  // ClpGeneratorUtilities,
+  // ClpParameterUtilities,
+  // ClpCipherUtilities,
+  // ClpNistObjectIdentifiers,
+  ClpBlockCipherModes,
+  ClpIBlockCipherModes,
+  ClpIBlockCipher,
+  // ClpBufferedBlockCipher,
+  // ClpIBufferedBlockCipher,
+  // ClpPaddedBufferedBlockCipher,
+  // ClpIPaddedBufferedBlockCipher,
+  // ClpPaddingModes,
+  // ClpIPaddingModes,
+  ClpEncoders,
+  ClpArrayUtils,
+  ClpCryptoLibTypes;
+
+type
+
+  TCryptoLibTestCase = class abstract(TTestCase)
+
+  end;
+
+type
+
+  TTestCTS = class(TCryptoLibTestCase)
+  private
+  var
+    Faes128, FaesIn1, FaesIn2, FaesIn3, FaesOut1, FaesOut2, FaesOut3,
+      FZeroIV: TCryptoLibByteArray;
+
+    procedure DoCTSTest(id: Int32; const cipher: IBlockCipher;
+      const params: ICipherParameters;
+      const input, output: TCryptoLibByteArray);
+
+  protected
+    procedure SetUp; override;
+    procedure TearDown; override;
+  published
+
+    procedure TestCTS;
+    procedure TestExceptions;
+
+  end;
+
+implementation
+
+{ TTestCTS }
+
+procedure TTestCTS.DoCTSTest(id: Int32; const cipher: IBlockCipher;
+  const params: ICipherParameters; const input, output: TCryptoLibByteArray);
+var
+  &out: TCryptoLibByteArray;
+  engine: IBufferedBlockCipher;
+  len: Int32;
+begin
+  System.SetLength(&out, System.length(input));
+
+  engine := TCTSBlockCipher.Create(cipher) as ICTSBlockCipher;
+
+  engine.Init(true, params);
+
+  len := engine.ProcessBytes(input, 0, System.length(input), &out, 0);
+
+  engine.doFinal(&out, len);
+
+  if not(TArrayUtils.AreEqual(output, &out)) then
+  begin
+    Fail(Format('Failed Encryption, ID %d Expected %s but got %s',
+      [id, THex.Encode(output), THex.Encode(&out)]));
+  end;
+
+  engine.Init(false, params);
+
+  len := engine.ProcessBytes(output, 0, System.length(output), &out, 0);
+
+  engine.doFinal(&out, len);
+
+  if not(TArrayUtils.AreEqual(input, &out)) then
+  begin
+    Fail(Format('Failed Decryption, ID %d Expected %s but got %s',
+      [id, THex.Encode(input), THex.Encode(&out)]));
+  end;
+end;
+
+procedure TTestCTS.SetUp;
+begin
+  inherited;
+  //
+  // test vectors from rfc3962
+  //
+  Faes128 := THex.Decode('636869636B656E207465726979616B69');
+  FaesIn1 := THex.Decode('4920776F756C64206C696B652074686520');
+  FaesOut1 := THex.Decode('C6353568F2BF8CB4D8A580362DA7FF7F97');
+  FaesIn2 := THex.Decode
+    ('4920776F756C64206C696B65207468652047656E6572616C20476175277320');
+  FaesOut2 := THex.Decode
+    ('FC00783E0EFDB2C1D445D4C8EFF7ED2297687268D6ECCCC0C07B25E25ECFE5');
+  FaesIn3 := THex.Decode
+    ('4920776F756C64206C696B65207468652047656E6572616C2047617527732043');
+  FaesOut3 := THex.Decode
+    ('39312523A78662D5BE7FCBCC98EBF5A897687268D6ECCCC0C07B25E25ECFE584');
+  System.SetLength(FZeroIV, 16);
+end;
+
+procedure TTestCTS.TearDown;
+begin
+  inherited;
+
+end;
+
+procedure TTestCTS.TestCTS;
+begin
+  DoCTSTest(1, TCBCBlockCipher.Create(TAESEngine.Create() as IAESEngine)
+    as ICBCBlockCipher, TParametersWithIV.Create(TKeyParameter.Create(Faes128)
+    as IKeyParameter, FZeroIV) as IParametersWithIV, FaesIn1, FaesOut1);
+  DoCTSTest(2, TCBCBlockCipher.Create(TAESEngine.Create() as IAESEngine)
+    as ICBCBlockCipher, TParametersWithIV.Create(TKeyParameter.Create(Faes128)
+    as IKeyParameter, FZeroIV) as IParametersWithIV, FaesIn2, FaesOut2);
+  DoCTSTest(3, TCBCBlockCipher.Create(TAESEngine.Create() as IAESEngine)
+    as ICBCBlockCipher, TParametersWithIV.Create(TKeyParameter.Create(Faes128)
+    as IKeyParameter, FZeroIV) as IParametersWithIV, FaesIn3, FaesOut3);
+end;
+
+procedure TTestCTS.TestExceptions;
+var
+  engine: IBufferedBlockCipher;
+begin
+  try
+    engine := TCTSBlockCipher.Create(TSICBlockCipher.Create(TAESEngine.Create()
+      as IAESEngine) as ISICBlockCipher);
+    Fail('Expected CTS construction error - only ECB/CBC supported.');
+  except
+    on e: EArgumentCryptoLibException do
+    begin
+      // expected
+    end;
+  end;
+end;
+
+initialization
+
+// Register any test cases with the test runner
+
+{$IFDEF FPC}
+  RegisterTest(TTestCTS);
+{$ELSE}
+  RegisterTest(TTestCTS.Suite);
+{$ENDIF FPC}
+
+end.

+ 353 - 39
CryptoLib/src/Crypto/Modes/ClpBlockCipherModes.pas

@@ -25,6 +25,7 @@ uses
   Math,
   Math,
   SysUtils,
   SysUtils,
   ClpIBlockCipher,
   ClpIBlockCipher,
+  ClpBufferedBlockCipher,
   ClpICipherParameters,
   ClpICipherParameters,
   ClpIParametersWithIV,
   ClpIParametersWithIV,
   ClpArrayUtils,
   ClpArrayUtils,
@@ -43,6 +44,9 @@ resourcestring
     'CTR/SIC mode requires IV no greater than: %u bytes';
     'CTR/SIC mode requires IV no greater than: %u bytes';
   SInvalidTooSmallIVLength = 'CTR/SIC mode requires IV of at least: %u bytes';
   SInvalidTooSmallIVLength = 'CTR/SIC mode requires IV of at least: %u bytes';
 {$ENDIF}
 {$ENDIF}
+  SUnsupportedCipher = 'CtsBlockCipher Can Only Accept ECB or CBC Ciphers';
+  SNegativeInputLength = 'Can''t Have a Negative Input Length!';
+  SCTSDoFinalError = 'Need at Least One Block of Input For CTS';
 
 
 type
 type
 
 
@@ -613,6 +617,98 @@ type
 
 
   end;
   end;
 
 
+type
+
+  /// <summary>
+  /// A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
+  /// be used to produce cipher text which is the same outLength as the plain
+  /// text.
+  /// </summary>
+  TCtsBlockCipher = class sealed(TBufferedBlockCipher, ICtsBlockCipher)
+
+  strict private
+    FblockSize: Int32;
+
+  public
+
+    /// <summary>
+    /// Create a buffered block cipher that uses Cipher Text Stealing
+    /// </summary>
+    /// <param name="cipher">
+    /// the underlying block cipher this buffering object wraps.
+    /// </param>
+    constructor Create(const cipher: IBlockCipher);
+
+    /// <summary>
+    /// return the size of the output buffer required for an update plus a
+    /// doFinal with an input of len bytes.
+    /// </summary>
+    /// <param name="inputLen">
+    /// the outLength of the input.
+    /// </param>
+    /// <returns>
+    /// the space required to accommodate a call to update and doFinal with
+    /// length bytes of input.
+    /// </returns>
+    function GetOutputSize(inputLen: Int32): Int32; override;
+
+    /// <summary>
+    /// return the size of the output buffer required for an update an input
+    /// of len bytes.
+    /// </summary>
+    /// <param name="inputLen">
+    /// the outLength of the input.
+    /// </param>
+    /// <returns>
+    /// the space required to accommodate a call to update with length bytes
+    /// of input.
+    /// </returns>
+    function GetUpdateOutputSize(inputLen: Int32): Int32; override;
+
+    function ProcessByte(input: Byte; const output: TCryptoLibByteArray;
+      outOff: Int32): Int32; 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 out.
+    /// </returns>
+    function ProcessBytes(const input: TCryptoLibByteArray; inOff, len: Int32;
+      const output: TCryptoLibByteArray; outOff: Int32): Int32; 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 out.
+    /// </returns>
+    function DoFinal(const output: TCryptoLibByteArray; outOff: Int32)
+      : Int32; override;
+
+  end;
+
 implementation
 implementation
 
 
 { TCbcBlockCipher }
 { TCbcBlockCipher }
@@ -631,17 +727,17 @@ end;
 function TCbcBlockCipher.DecryptBlock(const input: TCryptoLibByteArray;
 function TCbcBlockCipher.DecryptBlock(const input: TCryptoLibByteArray;
   inOff: Int32; const outBytes: TCryptoLibByteArray; outOff: Int32): Int32;
   inOff: Int32; const outBytes: TCryptoLibByteArray; outOff: Int32): Int32;
 var
 var
-  length, I: Int32;
+  Length, I: Int32;
   tmp: TCryptoLibByteArray;
   tmp: TCryptoLibByteArray;
 begin
 begin
-  if ((inOff + FblockSize) > System.length(input)) then
+  if ((inOff + FblockSize) > System.Length(input)) then
   begin
   begin
     raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
     raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
   end;
   end;
 
 
   System.Move(input[inOff], FcbcNextV[0], FblockSize * System.SizeOf(Byte));
   System.Move(input[inOff], FcbcNextV[0], FblockSize * System.SizeOf(Byte));
 
 
-  length := Fcipher.ProcessBlock(input, inOff, outBytes, outOff);
+  Length := Fcipher.ProcessBlock(input, inOff, outBytes, outOff);
 
 
 
 
   // XOR the FcbcV and the output
   // XOR the FcbcV and the output
@@ -666,7 +762,7 @@ function TCbcBlockCipher.EncryptBlock(const input: TCryptoLibByteArray;
 var
 var
   I, &length: Int32;
   I, &length: Int32;
 begin
 begin
-  if ((inOff + FblockSize) > System.length(input)) then
+  if ((inOff + FblockSize) > System.Length(input)) then
   begin
   begin
     raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
     raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
   end;
   end;
@@ -683,7 +779,7 @@ begin
 
 
   // copy ciphertext to FcbcV
   // copy ciphertext to FcbcV
 
 
-  System.Move(outBytes[outOff], FcbcV[0], System.length(FcbcV) *
+  System.Move(outBytes[outOff], FcbcV[0], System.Length(FcbcV) *
     System.SizeOf(Byte));
     System.SizeOf(Byte));
 
 
   result := &length;
   result := &length;
@@ -691,8 +787,8 @@ end;
 
 
 procedure TCbcBlockCipher.Reset;
 procedure TCbcBlockCipher.Reset;
 begin
 begin
-  System.Move(FIV[0], FcbcV[0], System.length(FIV));
-  TArrayUtils.Fill(FcbcNextV, 0, System.length(FcbcNextV), Byte(0));
+  System.Move(FIV[0], FcbcV[0], System.Length(FIV));
+  TArrayUtils.Fill(FcbcNextV, 0, System.Length(FcbcNextV), Byte(0));
 
 
   Fcipher.Reset();
   Fcipher.Reset();
 end;
 end;
@@ -733,12 +829,12 @@ begin
   begin
   begin
     iv := ivParam.GetIV();
     iv := ivParam.GetIV();
 
 
-    if (System.length(iv) <> FblockSize) then
+    if (System.Length(iv) <> FblockSize) then
     begin
     begin
       raise EArgumentCryptoLibException.CreateRes(@SInvalidIVLength);
       raise EArgumentCryptoLibException.CreateRes(@SInvalidIVLength);
     end;
     end;
 
 
-    System.Move(iv[0], FIV[0], System.length(iv) * System.SizeOf(Byte));
+    System.Move(iv[0], FIV[0], System.Length(iv) * System.SizeOf(Byte));
 
 
     Lparameters := ivParam.parameters;
     Lparameters := ivParam.parameters;
   end;
   end;
@@ -789,12 +885,12 @@ function TCfbBlockCipher.DecryptBlock(const input: TCryptoLibByteArray;
 var
 var
   I, count: Int32;
   I, count: Int32;
 begin
 begin
-  if ((inOff + FblockSize) > System.length(input)) then
+  if ((inOff + FblockSize) > System.Length(input)) then
   begin
   begin
     raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
     raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
   end;
   end;
 
 
-  if ((outOff + FblockSize) > System.length(outBytes)) then
+  if ((outOff + FblockSize) > System.Length(outBytes)) then
   begin
   begin
     raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
     raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
   end;
   end;
@@ -804,13 +900,13 @@ begin
   //
   //
   // change over the input block.
   // change over the input block.
   //
   //
-  count := (System.length(FcfbV) - FblockSize) * System.SizeOf(Byte);
+  count := (System.Length(FcfbV) - FblockSize) * System.SizeOf(Byte);
   if count > 0 then
   if count > 0 then
   begin
   begin
     System.Move(FcfbV[FblockSize], FcfbV[0], count);
     System.Move(FcfbV[FblockSize], FcfbV[0], count);
   end;
   end;
 
 
-  System.Move(input[inOff], FcfbV[(System.length(FcfbV) - FblockSize)],
+  System.Move(input[inOff], FcfbV[(System.Length(FcfbV) - FblockSize)],
     FblockSize * System.SizeOf(Byte));
     FblockSize * System.SizeOf(Byte));
 
 
   // XOR the FcfbV with the ciphertext producing the plaintext
   // XOR the FcfbV with the ciphertext producing the plaintext
@@ -828,12 +924,12 @@ function TCfbBlockCipher.EncryptBlock(const input: TCryptoLibByteArray;
 var
 var
   I, count: Int32;
   I, count: Int32;
 begin
 begin
-  if ((inOff + FblockSize) > System.length(input)) then
+  if ((inOff + FblockSize) > System.Length(input)) then
   begin
   begin
     raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
     raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
   end;
   end;
 
 
-  if ((outOff + FblockSize) > System.length(outBytes)) then
+  if ((outOff + FblockSize) > System.Length(outBytes)) then
   begin
   begin
     raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
     raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
   end;
   end;
@@ -850,14 +946,14 @@ begin
   //
   //
   // change over the input block.
   // change over the input block.
   //
   //
-  count := (System.length(FcfbV) - FblockSize) * System.SizeOf(Byte);
+  count := (System.Length(FcfbV) - FblockSize) * System.SizeOf(Byte);
 
 
   if count > 0 then
   if count > 0 then
   begin
   begin
     System.Move(FcfbV[FblockSize], FcfbV[0], count);
     System.Move(FcfbV[FblockSize], FcfbV[0], count);
   end;
   end;
 
 
-  System.Move(outBytes[outOff], FcfbV[(System.length(FcfbV) - FblockSize)],
+  System.Move(outBytes[outOff], FcfbV[(System.Length(FcfbV) - FblockSize)],
     FblockSize * System.SizeOf(Byte));
     FblockSize * System.SizeOf(Byte));
 
 
   result := FblockSize;
   result := FblockSize;
@@ -865,7 +961,7 @@ end;
 
 
 procedure TCfbBlockCipher.Reset;
 procedure TCfbBlockCipher.Reset;
 begin
 begin
-  System.Move(FIV[0], FcfbV[0], System.length(FIV));
+  System.Move(FIV[0], FcfbV[0], System.Length(FIV));
 
 
   Fcipher.Reset();
   Fcipher.Reset();
 end;
 end;
@@ -905,9 +1001,9 @@ begin
   begin
   begin
     iv := ivParam.GetIV();
     iv := ivParam.GetIV();
 
 
-    diff := System.length(FIV) - System.length(iv);
+    diff := System.Length(FIV) - System.Length(iv);
 
 
-    System.Move(iv[0], FIV[diff], System.length(iv) * System.SizeOf(Byte));
+    System.Move(iv[0], FIV[diff], System.Length(iv) * System.SizeOf(Byte));
     TArrayUtils.Fill(FIV, 0, diff, Byte(0));
     TArrayUtils.Fill(FIV, 0, diff, Byte(0));
 
 
     Lparameters := ivParam.parameters;
     Lparameters := ivParam.parameters;
@@ -952,7 +1048,7 @@ end;
 
 
 procedure TOfbBlockCipher.Reset;
 procedure TOfbBlockCipher.Reset;
 begin
 begin
-  System.Move(FIV[0], FofbV[0], System.length(FIV));
+  System.Move(FIV[0], FofbV[0], System.Length(FIV));
 
 
   Fcipher.Reset();
   Fcipher.Reset();
 
 
@@ -994,13 +1090,13 @@ begin
   begin
   begin
     iv := ivParam.GetIV();
     iv := ivParam.GetIV();
 
 
-    if (System.length(iv) < System.length(FIV)) then
+    if (System.Length(iv) < System.Length(FIV)) then
     begin
     begin
       // prepend the supplied IV with zeros (per FIPS PUB 81)
       // 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));
+      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
+      for I := 0 to System.Pred(System.Length(FIV) - System.Length(iv)) do
       begin
       begin
         FIV[I] := 0;
         FIV[I] := 0;
       end;
       end;
@@ -1008,7 +1104,7 @@ begin
     end
     end
     else
     else
     begin
     begin
-      System.Move(iv[0], FIV[0], System.length(FIV) * System.SizeOf(Byte));
+      System.Move(iv[0], FIV[0], System.Length(FIV) * System.SizeOf(Byte));
     end;
     end;
 
 
     Lparameters := ivParam.parameters;
     Lparameters := ivParam.parameters;
@@ -1029,12 +1125,12 @@ function TOfbBlockCipher.ProcessBlock(const input: TCryptoLibByteArray;
 var
 var
   I, count: Int32;
   I, count: Int32;
 begin
 begin
-  if ((inOff + FblockSize) > System.length(input)) then
+  if ((inOff + FblockSize) > System.Length(input)) then
   begin
   begin
     raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
     raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
   end;
   end;
 
 
-  if ((outOff + FblockSize) > System.length(output)) then
+  if ((outOff + FblockSize) > System.Length(output)) then
   begin
   begin
     raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
     raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
   end;
   end;
@@ -1054,14 +1150,14 @@ begin
   //
   //
   // change over the input block.
   // change over the input block.
   //
   //
-  count := (System.length(FofbV) - FblockSize) * System.SizeOf(Byte);
+  count := (System.Length(FofbV) - FblockSize) * System.SizeOf(Byte);
 
 
   if count > 0 then
   if count > 0 then
   begin
   begin
     System.Move(FofbV[FblockSize], FofbV[0], count);
     System.Move(FofbV[FblockSize], FofbV[0], count);
   end;
   end;
 
 
-  System.Move(FofbOutV[0], FofbV[(System.length(FofbV) - FblockSize)],
+  System.Move(FofbOutV[0], FofbV[(System.Length(FofbV) - FblockSize)],
     FblockSize * System.SizeOf(Byte));
     FblockSize * System.SizeOf(Byte));
 
 
   result := FblockSize;
   result := FblockSize;
@@ -1082,8 +1178,8 @@ end;
 
 
 procedure TSicBlockCipher.Reset;
 procedure TSicBlockCipher.Reset;
 begin
 begin
-  TArrayUtils.Fill(Fcounter, 0, System.length(Fcounter), Byte(0));
-  System.Move(FIV[0], Fcounter[0], System.length(FIV) * System.SizeOf(Byte));
+  TArrayUtils.Fill(Fcounter, 0, System.Length(Fcounter), Byte(0));
+  System.Move(FIV[0], Fcounter[0], System.Length(FIV) * System.SizeOf(Byte));
 
 
   Fcipher.Reset();
   Fcipher.Reset();
 
 
@@ -1125,7 +1221,7 @@ begin
   begin
   begin
     FIV := ivParam.GetIV();
     FIV := ivParam.GetIV();
 
 
-    if (FblockSize < System.length(FIV)) then
+    if (FblockSize < System.Length(FIV)) then
     begin
     begin
       raise EArgumentCryptoLibException.CreateResFmt(@SInvalidTooLargeIVLength,
       raise EArgumentCryptoLibException.CreateResFmt(@SInvalidTooLargeIVLength,
         [FblockSize]);
         [FblockSize]);
@@ -1133,7 +1229,7 @@ begin
 
 
     maxCounterSize := Min(8, FblockSize div 2);
     maxCounterSize := Min(8, FblockSize div 2);
 
 
-    if ((FblockSize - System.length(FIV)) > maxCounterSize) then
+    if ((FblockSize - System.Length(FIV)) > maxCounterSize) then
     begin
     begin
       raise EArgumentCryptoLibException.CreateResFmt(@SInvalidTooSmallIVLength,
       raise EArgumentCryptoLibException.CreateResFmt(@SInvalidTooSmallIVLength,
         [FblockSize - maxCounterSize]);
         [FblockSize - maxCounterSize]);
@@ -1163,12 +1259,12 @@ var
   I, J: Int32;
   I, J: Int32;
 begin
 begin
 
 
-  if ((inOff + FblockSize) > System.length(input)) then
+  if ((inOff + FblockSize) > System.Length(input)) then
   begin
   begin
     raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
     raise EDataLengthCryptoLibException.CreateRes(@SInputBufferTooShort);
   end;
   end;
 
 
-  if ((outOff + FblockSize) > System.length(output)) then
+  if ((outOff + FblockSize) > System.Length(output)) then
   begin
   begin
     raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
     raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
   end;
   end;
@@ -1178,14 +1274,14 @@ begin
   //
   //
   // XOR the counterOut with the plaintext producing the cipher text
   // XOR the counterOut with the plaintext producing the cipher text
   //
   //
-  for I := 0 to System.Pred(System.length(FcounterOut)) do
+  for I := 0 to System.Pred(System.Length(FcounterOut)) do
   begin
   begin
 
 
     output[outOff + I] := Byte(FcounterOut[I] xor input[inOff + I]);
     output[outOff + I] := Byte(FcounterOut[I] xor input[inOff + I]);
   end;
   end;
 
 
   // Increment the counter
   // Increment the counter
-  J := System.length(Fcounter);
+  J := System.Length(Fcounter);
   System.Dec(J);
   System.Dec(J);
   System.Inc(Fcounter[J]);
   System.Inc(Fcounter[J]);
   while ((J >= 0) and (Fcounter[J] = 0)) do
   while ((J >= 0) and (Fcounter[J] = 0)) do
@@ -1194,7 +1290,225 @@ begin
     System.Inc(Fcounter[J]);
     System.Inc(Fcounter[J]);
   end;
   end;
 
 
-  result := System.length(Fcounter);
+  result := System.Length(Fcounter);
+end;
+
+{ TCtsBlockCipher }
+
+constructor TCtsBlockCipher.Create(const cipher: IBlockCipher);
+begin
+  Inherited Create();
+  if Supports(cipher, ICfbBlockCipher) or Supports(cipher, IOfbBlockCipher) or
+    Supports(cipher, ISicBlockCipher) or Supports(cipher, ICtsBlockCipher) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SUnsupportedCipher);
+  end;
+
+  Fcipher := cipher;
+
+  FblockSize := Fcipher.GetBlockSize();
+  System.SetLength(Fbuf, FblockSize * 2);
+  FbufOff := 0;
+end;
+
+function TCtsBlockCipher.DoFinal(const output: TCryptoLibByteArray;
+  outOff: Int32): Int32;
+var
+  blockSize, len, I: Int32;
+  block, lastBlock: TCryptoLibByteArray;
+  c: IBlockCipher;
+begin
+  if ((FbufOff + outOff) > System.Length(output)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes
+      (@SOutputBufferTooSmallForDoFinal);
+  end;
+
+  blockSize := Fcipher.GetBlockSize();
+  len := FbufOff - blockSize;
+  System.SetLength(block, blockSize);
+
+  if (FforEncryption) then
+  begin
+    if (FbufOff < blockSize) then
+    begin
+      raise EDataLengthCryptoLibException.CreateRes(@SCTSDoFinalError);
+    end;
+
+    Fcipher.ProcessBlock(Fbuf, 0, block, 0);
+
+    if (FbufOff > blockSize) then
+    begin
+
+      I := FbufOff;
+      while I <> System.Length(Fbuf) do
+      begin
+        Fbuf[I] := block[I - blockSize];
+        System.Inc(I);
+      end;
+
+      I := blockSize;
+      while I <> FbufOff do
+      begin
+        Fbuf[I] := Fbuf[I] xor (block[I - blockSize]);
+        System.Inc(I);
+      end;
+
+      if Supports(Fcipher, ICbcBlockCipher) then
+      begin
+        c := (Fcipher as ICbcBlockCipher).GetUnderlyingCipher();
+
+        c.ProcessBlock(Fbuf, blockSize, output, outOff);
+      end
+      else
+      begin
+        Fcipher.ProcessBlock(Fbuf, blockSize, output, outOff);
+      end;
+
+      System.Move(block[0], output[outOff + blockSize],
+        len * System.SizeOf(Byte));
+    end
+    else
+    begin
+      System.Move(block[0], output[outOff], blockSize * System.SizeOf(Byte));
+    end;
+  end
+  else
+  begin
+
+    if (FbufOff < blockSize) then
+    begin
+      raise EDataLengthCryptoLibException.CreateRes(@SCTSDoFinalError);
+    end;
+
+    System.SetLength(lastBlock, blockSize);
+
+    if (FbufOff > blockSize) then
+    begin
+
+      if Supports(Fcipher, ICbcBlockCipher) then
+      begin
+        c := (Fcipher as ICbcBlockCipher).GetUnderlyingCipher();
+
+        c.ProcessBlock(Fbuf, 0, block, 0);
+      end
+      else
+      begin
+        Fcipher.ProcessBlock(Fbuf, 0, block, 0);
+      end;
+
+      I := blockSize;
+      while I <> FbufOff do
+      begin
+        lastBlock[I - blockSize] := Byte(block[I - blockSize] xor Fbuf[I]);
+        System.Inc(I);
+      end;
+
+      System.Move(Fbuf[blockSize], block[0], len * System.SizeOf(Byte));
+      Fcipher.ProcessBlock(block, 0, output, outOff);
+      System.Move(lastBlock[0], output[outOff + blockSize],
+        len * System.SizeOf(Byte));
+    end
+    else
+    begin
+      Fcipher.ProcessBlock(Fbuf, 0, block, 0);
+      System.Move(block[0], output[outOff], blockSize * System.SizeOf(Byte));
+    end;
+
+  end;
+
+  result := FbufOff;
+
+  Reset();
+end;
+
+function TCtsBlockCipher.GetOutputSize(inputLen: Int32): Int32;
+begin
+  result := inputLen + FbufOff;
+end;
+
+function TCtsBlockCipher.GetUpdateOutputSize(inputLen: Int32): Int32;
+var
+  total, leftOver: Int32;
+begin
+  total := inputLen + FbufOff;
+  leftOver := total mod System.Length(Fbuf);
+
+  if (leftOver = 0) then
+  begin
+    result := total - System.Length(Fbuf);
+    Exit;
+  end;
+  result := total - leftOver;
+end;
+
+function TCtsBlockCipher.ProcessByte(input: Byte;
+  const output: TCryptoLibByteArray; outOff: Int32): Int32;
+begin
+  result := 0;
+
+  if (FbufOff = System.Length(Fbuf)) then
+  begin
+    result := Fcipher.ProcessBlock(Fbuf, 0, output, outOff);
+    System.Move(Fbuf[FblockSize], Fbuf[0], FblockSize * System.SizeOf(Byte));
+    FbufOff := FblockSize;
+  end;
+
+  Fbuf[FbufOff] := input;
+  System.Inc(FbufOff);
+end;
+
+function TCtsBlockCipher.ProcessBytes(const input: TCryptoLibByteArray;
+  inOff, len: Int32; const output: TCryptoLibByteArray; outOff: Int32): Int32;
+var
+  blockSize, Length, gapLen: Int32;
+begin
+  if (len < 0) then
+  begin
+    raise EInvalidArgument.CreateRes(@SNegativeInputLength);
+  end;
+
+  blockSize := GetBlockSize();
+  Length := GetUpdateOutputSize(len);
+
+  if (Length > 0) then
+  begin
+    if ((outOff + Length) > System.Length(output)) then
+    begin
+      raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooShort);
+    end;
+  end;
+
+  result := 0;
+  gapLen := System.Length(Fbuf) - FbufOff;
+
+  if (len > gapLen) then
+  begin
+    System.Move(input[inOff], Fbuf[FbufOff], gapLen * System.SizeOf(Byte));
+
+    result := result + Fcipher.ProcessBlock(Fbuf, 0, output, outOff);
+    System.Move(Fbuf[blockSize], Fbuf[0], blockSize * System.SizeOf(Byte));
+
+    FbufOff := blockSize;
+
+    len := len - gapLen;
+    inOff := inOff + gapLen;
+
+    while (len > blockSize) do
+    begin
+      System.Move(input[inOff], Fbuf[FbufOff], blockSize * System.SizeOf(Byte));
+      result := result + Fcipher.ProcessBlock(Fbuf, 0, output, outOff + result);
+      System.Move(Fbuf[blockSize], Fbuf[0], blockSize * System.SizeOf(Byte));
+
+      len := len - blockSize;
+      inOff := inOff + blockSize;
+    end;
+  end;
+
+  System.Move(input[inOff], Fbuf[FbufOff], len * System.SizeOf(Byte));
+
+  FbufOff := FbufOff + len;
+
 end;
 end;
 
 
 end.
 end.

+ 23 - 1
CryptoLib/src/Interfaces/ClpIBlockCipherModes.pas

@@ -22,7 +22,9 @@ unit ClpIBlockCipherModes;
 interface
 interface
 
 
 uses
 uses
-  ClpIBlockCipher;
+  ClpIBlockCipher,
+  ClpIBufferedBlockCipher,
+  ClpCryptoLibTypes;
 
 
 type
 type
   ICbcBlockCipher = interface(IBlockCipher)
   ICbcBlockCipher = interface(IBlockCipher)
@@ -84,6 +86,26 @@ type
 
 
   end;
   end;
 
 
+type
+  ICtsBlockCipher = interface(IBufferedBlockCipher)
+
+    ['{4D02FD0B-47D6-4914-B31F-5869FF364558}']
+
+    function GetOutputSize(inputLen: Int32): Int32;
+
+    function GetUpdateOutputSize(inputLen: Int32): Int32;
+
+    function ProcessByte(input: Byte; const output: TCryptoLibByteArray;
+      outOff: Int32): Int32;
+
+    function ProcessBytes(const input: TCryptoLibByteArray;
+      inOff, Length: Int32; const output: TCryptoLibByteArray;
+      outOff: Int32): Int32;
+
+    function DoFinal(const output: TCryptoLibByteArray; outOff: Int32): Int32;
+
+  end;
+
 implementation
 implementation
 
 
 end.
 end.

+ 22 - 4
CryptoLib/src/Security/ClpCipherUtilities.pas

@@ -71,10 +71,10 @@ type
   type
   type
 {$SCOPEDENUMS ON}
 {$SCOPEDENUMS ON}
     TCipherAlgorithm = (AES, BLOWFISH, SALSA20);
     TCipherAlgorithm = (AES, BLOWFISH, SALSA20);
-    TCipherMode = (NONE, CBC, CFB, CTR, ECB, OFB, SIC);
+    TCipherMode = (NONE, CBC, CFB, CTR, CTS, ECB, OFB, SIC);
     TCipherPadding = (NOPADDING, ISO10126PADDING, ISO10126D2PADDING,
     TCipherPadding = (NOPADDING, ISO10126PADDING, ISO10126D2PADDING,
       ISO10126_2PADDING, ISO7816_4PADDING, ISO9797_1PADDING, PKCS5,
       ISO10126_2PADDING, ISO7816_4PADDING, ISO9797_1PADDING, PKCS5,
-      PKCS5PADDING, PKCS7, PKCS7PADDING, TBCPADDING, X923PADDING,
+      PKCS5PADDING, PKCS7, PKCS7PADDING, TBCPADDING, WITHCTS, X923PADDING,
       ZEROBYTEPADDING);
       ZEROBYTEPADDING);
 {$SCOPEDENUMS OFF}
 {$SCOPEDENUMS OFF}
 
 
@@ -192,7 +192,7 @@ class function TCipherUtilities.GetCipher(algorithm: String): IBufferedCipher;
 var
 var
   aliased, algorithmName, temp, paddingName, mode, modeName: string;
   aliased, algorithmName, temp, paddingName, mode, modeName: string;
   di, LowPoint, bits, HighPoint: Int32;
   di, LowPoint, bits, HighPoint: Int32;
-  padded: Boolean;
+  padded, CTS: Boolean;
   parts: TCryptoLibStringArray;
   parts: TCryptoLibStringArray;
   cipherAlgorithm: TCipherAlgorithm;
   cipherAlgorithm: TCipherAlgorithm;
   cipherPadding: TCipherPadding;
   cipherPadding: TCipherPadding;
@@ -263,6 +263,7 @@ begin
     Exit;
     Exit;
   end;
   end;
 
 
+  CTS := False;
   padded := true;
   padded := true;
   padding := Nil;
   padding := Nil;
 
 
@@ -280,7 +281,7 @@ begin
     case cipherPadding of
     case cipherPadding of
       TCipherPadding.NOPADDING:
       TCipherPadding.NOPADDING:
         begin
         begin
-          padded := false;
+          padded := False;
         end;
         end;
 
 
       TCipherPadding.ISO10126PADDING, TCipherPadding.ISO10126D2PADDING,
       TCipherPadding.ISO10126PADDING, TCipherPadding.ISO10126D2PADDING,
@@ -305,6 +306,11 @@ begin
           padding := TTBCPadding.Create() as ITBCPadding;
           padding := TTBCPadding.Create() as ITBCPadding;
         end;
         end;
 
 
+      TCipherPadding.WITHCTS:
+        begin
+          CTS := true;
+        end;
+
       TCipherPadding.X923PADDING:
       TCipherPadding.X923PADDING:
         begin
         begin
           padding := TX923Padding.Create() as IX923Padding;
           padding := TX923Padding.Create() as IX923Padding;
@@ -393,6 +399,12 @@ begin
           blockCipher := TSicBlockCipher.Create(blockCipher) as ISicBlockCipher;
           blockCipher := TSicBlockCipher.Create(blockCipher) as ISicBlockCipher;
         end;
         end;
 
 
+      TCipherMode.CTS:
+        begin
+          CTS := true;
+          blockCipher := TCbcBlockCipher.Create(blockCipher) as ICbcBlockCipher;
+        end;
+
       TCipherMode.OFB:
       TCipherMode.OFB:
         begin
         begin
           if (di < 0) then
           if (di < 0) then
@@ -433,6 +445,12 @@ begin
   if (blockCipher <> Nil) then
   if (blockCipher <> Nil) then
   begin
   begin
 
 
+    if (CTS) then
+    begin
+      Result := TCtsBlockCipher.Create(blockCipher) as ICtsBlockCipher;
+      Exit;
+    end;
+
     if (padding <> Nil) then
     if (padding <> Nil) then
     begin
     begin
       Result := TPaddedBufferedBlockCipher.Create(blockCipher, padding)
       Result := TPaddedBufferedBlockCipher.Create(blockCipher, padding)