{*****************************************************************************
The DEC team (see file NOTICE.txt) licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. A copy of this licence is found in the root directory
of this project in the file LICENCE.txt or alternatively at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*****************************************************************************}
unit DECCipherModes;
interface
{$INCLUDE DECOptions.inc}
uses
{$IFDEF FPC}
SysUtils,
{$ELSE}
System.SysUtils,
{$ENDIF}
DECCipherBase;
{$I DECOptions.inc}
type
///
/// Most ciphers are block oriented and thus work on blocks of a fixed size.
/// In order to not encrypt each block separately without any link to his
/// predecessor and sucessor, which would make attacks on the encrypted data
/// easier, each block should be linked with his predecessor (or the
/// initialization vector). This class implements the various supported
/// algorithms for linking blocks.
///
TDECCipherModes = class(TDECCipher)
strict protected
///
/// Raises an EDECCipherException exception and provides the correct values
/// for message lenght and block size
///
procedure ReportInvalidMessageLength(Cipher: TDECCipher);
///
/// Electronic Code Book
/// Mode cmECBx needs message padding to be a multiple of Cipher.BlockSize
/// and should be used only in 1-byte Streamciphers.
/// This one works on Blocks of Cipher.BufferSize bytes, when using a
/// Blockcipher that's equal to Cipher.BlockSize.
///
///
/// This mode should not be used in practice, as it makes the encrypted
/// message vulnerable to certain attacks without knowing the encryption key
///
procedure EncodeECBx(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// 8bit Output Feedback mode, needs no padding
///
procedure EncodeOFB8(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// 8 bit Cipher Feedback mode, needs no padding and works on 8 bit
/// Feedback Shift Registers.
///
procedure EncodeCFB8(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// 8Bit CFS, double Cipher Feedback mode (CFB), needs no padding and
/// works on 8 bit Feedback Shift Registers.
/// This one is a proprietary mode developed by Hagen Reddmann. This mode
/// works as cmCBCx, cmCFBx, cmCFB8 but with double XOR'ing of the
/// inputstream into Feedback register.
///
procedure EncodeCFS8(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// Cipher Feedback mode (CFB) on Blocksize of Cipher, needs no padding
/// This one works on Blocks of Cipher.BufferSize bytes, when using a
/// Blockcipher that's equal to Cipher.BlockSize.
///
procedure EncodeCFBx(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// Output Feedback mode on Blocksize of Cipher, needs no padding and
/// works on 8 bit Feedback Shift Registers.
/// This one works on Blocks of Cipher.BufferSize bytes, when using a
/// Blockcipher that's equal to Cipher.BlockSize.
///
procedure EncodeOFBx(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// double Cipher Feedback mode (CFB) on Blocksize of Cipher, needs no padding.
/// This one works on Blocks of Cipher.BufferSize bytes, when using a
/// Blockcipher that's equal to Cipher.BlockSize.
/// This one is a proprietary mode developed by Hagen Reddmann. This mode
/// works as cmCBCx, cmCFBx, cmCFB8 but with double XOR'ing of the
/// inputstream into Feedback register.
///
procedure EncodeCFSx(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// Cipher Block Chaining, with CFB8 padding of truncated final block
/// It needs no external padding, because internally the last
/// truncated block is padded by cmCFS8 or cmCFB8. After padding these Modes
/// cannot be used to process any more data. If needed to process chunks of
/// data then each chunk must be aligned to Cipher.BufferSize bytes.
/// This one works on Blocks of Cipher.BufferSize bytes, when using a
/// Blockcipher that's equal to Cipher.BlockSize.
///
procedure EncodeCBCx(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// double CBC, with CFS8 padding of truncated final block
/// It needs no external padding, because internally the last
/// truncated block is padded by cmCFS8 or cmCFB8. After padding these Modes
/// cannot be used to process any more data. If needed to process chunks of
/// data then each chunk must be aligned to Cipher.BufferSize bytes.
/// This one works on Blocks of Cipher.BufferSize bytes, when using a
/// Blockcipher that's equal to Cipher.BlockSize.
/// This one is a proprietary mode developed by Hagen Reddmann. This mode
/// works as cmCBCx, cmCFBx, cmCFB8 but with double XOR'ing of the
/// inputstream into Feedback register.
///
procedure EncodeCTSx(Source, Dest: PByteArray; Size: Integer); virtual;
{$IFDEF DEC3_CMCTS}
///
/// double CBC, with
/// for DEC 3.0 compatibility only
/// This is a proprietary mode developed by Frederik Winkelsdorf. It
/// replaces the CFS8 padding of the truncated final block with a CFSx padding.
/// Useful when converting projects that previously used the old DEC v3.0. It
/// has the same restrictions for external padding and chunk processing as
/// cmCTSx has. It has a less secure padding of the truncated final block.
/// (to enable it see DECOptions.inc)
///
procedure EncodeCTS3(Source, Dest: PByteArray; Size: Integer); virtual;
{$ENDIF}
///
/// Electronic Code Book
/// Mode cmECBx needs message padding to be a multiple of Cipher.BlockSize
/// and should be used only in 1-byte Streamciphers.
/// This one works on blocks of Cipher.BufferSize bytes, when using a
/// blockcipher that's equal to Cipher.BlockSize.
///
procedure DecodeECBx(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// 8 bit Output Feedback mode, needs no padding
///
procedure DecodeOFB8(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// 8 bit Cipher Feedback mode, needs no padding and works on 8 bit
/// Feedback Shift Registers.
///
procedure DecodeCFB8(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// 8 Bit CFS, double Cipher Feedback mode (CFB), needs no padding and
/// works on 8 bit Feedback Shift Registers.
/// This one is a proprietary mode developed by Hagen Reddmann. This mode
/// works as cmCBCx, cmCFBx, cmCFB8 but with double XOR'ing of the
/// inputstream into Feedback register.
///
procedure DecodeCFS8(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// Cipher Feedback mode (CFB) on Blocksize of Cipher, needs no padding
/// This one works on blocks of Cipher.BufferSize bytes, when using a
/// blockcipher that's equal to Cipher.BlockSize.
///
procedure DecodeCFBx(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// Output Feedback mode on Blocksize of Cipher, needs no padding and
/// works on 8 bit Feedback Shift Registers.
/// This one works on blocks of Cipher.BufferSize bytes, when using a
/// blockcipher that's equal to Cipher.BlockSize.
///
procedure DecodeOFBx(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// double Cipher Feedback mode (CFB) on Blocksize of Cipher, needs no padding.
/// This one works on blocks of Cipher.BufferSize bytes, when using a
/// blockcipher that's equal to Cipher.BlockSize.
/// This one is a proprietary mode developed by Hagen Reddmann. This mode
/// works as cmCBCx, cmCFBx, cmCFB8 but with double XOR'ing of the
/// inputstream into Feedback register.
///
procedure DecodeCFSx(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// Cipher Block Chaining, with CFB8 padding of truncated final block.
/// It needs no external padding, because internally the last
/// truncated block is padded by cmCFS8 or cmCFB8. After padding these modes
/// cannot be used to process any more data. If needed to process chunks of
/// data then each chunk must be algined to Cipher.BufferSize bytes.
/// This one works on blocks of Cipher.BufferSize bytes, when using a
/// blockcipher that's equal to Cipher.BlockSize.
///
procedure DecodeCBCx(Source, Dest: PByteArray; Size: Integer); virtual;
///
/// double CBC, with CFS8 padding of truncated final block
/// It needs no external padding, because internally the last
/// truncated block is padded by cmCFS8 or cmCFB8. After padding these Modes
/// cannot be used to process any more data. If needed to process chunks of
/// data then each chunk must be algined to Cipher.BufferSize bytes.
/// This one works on blocks of Cipher.BufferSize bytes, when using a
/// blockcipher that's equal to Cipher.BlockSize.
/// This one is a proprietary mode developed by Hagen Reddmann. This mode
/// works as cmCBCx, cmCFBx, cmCFB8 but with double XOR'ing of the
/// inputstream into feedback register.
///
procedure DecodeCTSx(Source, Dest: PByteArray; Size: Integer); virtual;
{$IFDEF DEC3_CMCTS}
///
/// double CBC
/// This is a proprietary mode developed by Frederik Winkelsdorf. It
/// replaces the CFS8 padding of the truncated final block with a CFSx padding.
/// Useful when converting projects that previously used the old DEC v3.0. It
/// has the same restrictions for external padding and chunk processing as
/// cmCTSx has. It has a less secure padding of the truncated final block.
/// (to enable it see DECOptions.inc)
///
///
/// For DEC 3.0 compatibility only
///
procedure DecodeCTS3(Source, Dest: PByteArray; Size: Integer); virtual;
{$ENDIF}
public
///
/// Encrypts a given block of data
///
///
/// Data to be encrypted
///
///
/// Data after encryption
///
///
/// Size of the data the Source parameter points to in byte
///
procedure Encode(const Source; var Dest; DataSize: Integer);
///
/// Decrypts a given block of data
///
///
/// Data to be Decrypted
///
///
/// Data after decryption
///
///
/// Size of the data the Source parameter points to in byte
///
procedure Decode(const Source; var Dest; DataSize: Integer);
end;
implementation
uses
{$IFDEF FPC}
TypInfo,
{$ELSE}
System.TypInfo,
{$ENDIF}
DECUtil;
resourcestring
sInvalidMessageLength = 'Message length for mode %0:s must be a multiple of %1:d bytes';
procedure TDECCipherModes.ReportInvalidMessageLength(Cipher: TDECCipher);
begin
raise EDECCipherException.CreateResFmt(@sInvalidMessageLength,
[System.TypInfo.GetEnumName(TypeInfo(TCipherMode),
Integer(Cipher.Mode)),
Cipher.Context.BlockSize]);
end;
procedure TDECCipherModes.Encode(const Source; var Dest; DataSize: Integer);
begin
CheckState([csInitialized, csEncode, csDone]);
case FMode of
cmECBx: EncodeECBx(@Source, @Dest, DataSize);
cmCBCx: EncodeCBCx(@Source, @Dest, DataSize);
cmCTSx: EncodeCTSx(@Source, @Dest, DataSize);
{$IFDEF DEC3_CMCTS}
cmCTS3: EncodeCTS3(@Source, @Dest, DataSize);
{$ENDIF DEC3_CMCTS}
cmCFB8: EncodeCFB8(@Source, @Dest, DataSize);
cmCFBx: EncodeCFBx(@Source, @Dest, DataSize);
cmOFB8: EncodeOFB8(@Source, @Dest, DataSize);
cmOFBx: EncodeOFBx(@Source, @Dest, DataSize);
cmCFS8: EncodeCFS8(@Source, @Dest, DataSize);
cmCFSx: EncodeCFSx(@Source, @Dest, DataSize);
end;
end;
procedure TDECCipherModes.EncodeECBx(Source, Dest: PByteArray; Size: Integer);
var
I: Integer;
begin
if Context.BlockSize = 1 then
begin
DoEncode(Source, Dest, Size);
FState := csEncode;
end
else
begin
Dec(Size, FBufferSize);
I := 0;
while I <= Size do
begin
DoEncode(@Source[I], @Dest[I], FBufferSize);
Inc(I, FBufferSize);
end;
Dec(Size, I - FBufferSize);
if Size > 0 then
begin
if Size mod Context.BlockSize = 0 then
begin
DoEncode(@Source[I], @Dest[I], Size);
FState := csEncode;
end
else
begin
FState := csPadded;
ReportInvalidMessageLength(Self);
end;
end;
end;
end;
procedure TDECCipherModes.EncodeOFB8(Source, Dest: PByteArray; Size: Integer);
var
I: Integer;
begin
I := 0;
while I < Size do
begin
DoEncode(FFeedback, FBuffer, FBufferSize);
Move(FFeedback[1], FFeedback[0], FBufferSize - 1);
FFeedback[FBufferSize - 1] := FBuffer[0];
Dest[I] := Source[I] xor FBuffer[0];
Inc(I);
end;
FState := csEncode;
end;
procedure TDECCipherModes.EncodeCFB8(Source, Dest: PByteArray; Size: Integer);
// CFB-8
var
I: Integer;
begin
I := 0;
while I < Size do
begin
DoEncode(FFeedback, FBuffer, FBufferSize);
Move(FFeedback[1], FFeedback[0], FBufferSize - 1);
Dest[I] := Source[I] xor FBuffer[0];
FFeedback[FBufferSize - 1] := Dest[I];
Inc(I);
end;
FState := csEncode;
end;
procedure TDECCipherModes.EncodeCFS8(Source, Dest: PByteArray; Size: Integer);
// CFS-8, CTS as CFB
var
I: Integer;
begin
I := 0;
while I < Size do
begin
DoEncode(FFeedback, FBuffer, FBufferSize);
Dest[I] := Source[I] xor FBuffer[0];
Move(FFeedback[1], FFeedback[0], FBufferSize - 1);
FFeedback[FBufferSize - 1] := FFeedback[FBufferSize - 1] xor Dest[I];
Inc(I);
end;
FState := csEncode;
end;
procedure TDECCipherModes.EncodeCFBx(Source, Dest: PByteArray; Size: Integer);
// CFB-BlockSize
var
I: Integer;
F: PByteArray;
begin
FState := csEncode;
if FBufferIndex > 0 then
begin
I := FBufferSize - FBufferIndex;
if I > Size then
I := Size;
XORBuffers(Source[0], FBuffer[FBufferIndex], I, Dest[0]);
Move(Dest[0], FFeedback[FBufferIndex], I);
Inc(FBufferIndex, I);
if FBufferIndex < FBufferSize then
Exit;
Dec(Size, I);
Source := @Source[I];
Dest := @Dest[I];
FBufferIndex := 0
end;
Dec(Size, FBufferSize);
F := FFeedback;
I := 0;
while I < Size do
begin
DoEncode(F, FBuffer, FBufferSize);
XORBuffers(Source[I], FBuffer[0], FBufferSize, Dest[I]);
F := @Dest[I];
Inc(I, FBufferSize);
end;
if F <> FFeedback then
Move(F^, FFeedback^, FBufferSize);
Dec(Size, I - FBufferSize);
if Size > 0 then
begin
DoEncode(FFeedback, FBuffer, FBufferSize);
XORBuffers(Source[I], FBuffer[0], Size, Dest[I]);
Move(Dest[I], FFeedback[0], Size);
FBufferIndex := Size;
end;
end;
procedure TDECCipherModes.EncodeOFBx(Source, Dest: PByteArray; Size: Integer);
// OFB-BlockSize
var
I: Integer;
begin
FState := csEncode;
if FBufferIndex > 0 then
begin
I := FBufferSize - FBufferIndex;
if I > Size then
I := Size;
XORBuffers(Source[0], FFeedback[FBufferIndex], I, Dest[0]);
Inc(FBufferIndex, I);
if FBufferIndex < FBufferSize then
Exit;
Dec(Size, I);
Source := @Source[I];
Dest := @Dest[I];
FBufferIndex := 0
end;
Dec(Size, FBufferSize);
I := 0;
while I < Size do
begin
DoEncode(FFeedback, FFeedback, FBufferSize);
XORBuffers(Source[I], FFeedback[0], FBufferSize, Dest[I]);
Inc(I, FBufferSize);
end;
Dec(Size, I - FBufferSize);
if Size > 0 then
begin
DoEncode(FFeedback, FFeedback, FBufferSize);
XORBuffers(Source[I], FFeedback[0], Size, Dest[I]);
FBufferIndex := Size;
end;
end;
procedure TDECCipherModes.EncodeCFSx(Source, Dest: PByteArray; Size: Integer);
// CFS-BlockSize
var
I: Integer;
begin
FState := csEncode;
if FBufferIndex > 0 then
begin
I := FBufferSize - FBufferIndex;
if I > Size then
I := Size;
XORBuffers(Source[0], FBuffer[FBufferIndex], I, Dest[0]);
XORBuffers(Dest[0], FFeedback[FBufferIndex], I, FFeedback[FBufferIndex]);
Inc(FBufferIndex, I);
if FBufferIndex < FBufferSize then
Exit;
Dec(Size, I);
Source := @Source[I];
Dest := @Dest[I];
FBufferIndex := 0
end;
Dec(Size, FBufferSize);
I := 0;
while I < Size do
begin
DoEncode(FFeedback, FBuffer, FBufferSize);
XORBuffers(Source[I], FBuffer[0], FBufferSize, Dest[I]);
XORBuffers(Dest[I], FFeedback[0], FBufferSize, FFeedback[0]);
Inc(I, FBufferSize);
end;
Dec(Size, I - FBufferSize);
if Size > 0 then
begin
DoEncode(FFeedback, FBuffer, FBufferSize);
XORBuffers(Source[I], FBuffer[0], Size, Dest[I]);
XORBuffers(Dest[I], FFeedback[0], Size, FFeedback[0]);
FBufferIndex := Size;
end;
end;
procedure TDECCipherModes.EncodeCBCx(Source, Dest: PByteArray; Size: Integer);
var
F: PByteArray;
I: Integer;
begin
Dec(Size, FBufferSize);
F := FFeedback;
I := 0;
while I <= Size do
begin
XORBuffers(Source[I], F[0], FBufferSize, Dest[I]);
F := @Dest[I];
DoEncode(F, F, FBufferSize);
Inc(I, FBufferSize);
end;
if F <> FFeedback then
Move(F[0], FFeedback[0], FBufferSize);
Dec(Size, I - FBufferSize);
if Size > 0 then
begin // padding
EncodeCFB8(@Source[I], @Dest[I], Size);
FState := csPadded;
end
else
FState := csEncode;
end;
procedure TDECCipherModes.EncodeCTSx(Source, Dest: PByteArray; Size: Integer);
var
I: Integer;
begin
Dec(Size, FBufferSize);
I := 0;
while I <= Size do
begin
XORBuffers(Source[I], FFeedback[0], FBufferSize, Dest[I]);
DoEncode(@Dest[I], @Dest[I], FBufferSize);
XORBuffers(Dest[I], FFeedback[0], FBufferSize, FFeedback[0]);
Inc(I, FBufferSize);
end;
Dec(Size, I - FBufferSize);
if Size > 0 then
begin // padding
EncodeCFS8(@Source[I], @Dest[I], Size);
FState := csPadded;
end
else
FState := csEncode;
end;
{$IFDEF DEC3_CMCTS}
procedure TDECCipherModes.EncodeCTS3(Source, Dest: PByteArray; Size: Integer);
var
I: Integer;
begin
Dec(Size, FBufferSize);
I := 0;
while I <= Size do
begin
XORBuffers(Source[I], FFeedback[0], FBufferSize, Dest[I]);
DoEncode(@Dest[I], @Dest[I], FBufferSize);
XORBuffers(Dest[I], FFeedback[0], FBufferSize, FFeedback[0]);
Inc(I, FBufferSize);
end;
Dec(Size, I - FBufferSize);
if Size > 0 then
begin // padding
EncodeCFSx(@Source[I], @Dest[I], Size); // use the padding implemented in CFSx
FState := csPadded;
end
else
FState := csEncode;
end;
{$ENDIF DEC3_CMCTS}
procedure TDECCipherModes.Decode(const Source; var Dest; DataSize: Integer);
begin
CheckState([csInitialized, csDecode, csDone]);
case FMode of
cmECBx: DecodeECBx(@Source, @Dest, DataSize);
cmCBCx: DecodeCBCx(@Source, @Dest, DataSize);
cmCTSx: DecodeCTSx(@Source, @Dest, DataSize);
{$IFDEF DEC3_CMCTS}
cmCTS3: DecodeCTS3(@Source, @Dest, DataSize);
{$ENDIF DEC3_CMCTS}
cmCFB8: DecodeCFB8(@Source, @Dest, DataSize);
cmCFBx: DecodeCFBx(@Source, @Dest, DataSize);
cmOFB8: DecodeOFB8(@Source, @Dest, DataSize);
cmOFBx: DecodeOFBx(@Source, @Dest, DataSize);
cmCFS8: DecodeCFS8(@Source, @Dest, DataSize);
cmCFSx: DecodeCFSx(@Source, @Dest, DataSize);
end;
end;
procedure TDECCipherModes.DecodeECBx(Source, Dest: PByteArray; Size: Integer);
var
I: Integer;
begin
if Context.BlockSize = 1 then
begin
DoDecode(Source, Dest, Size);
FState := csDecode;
end
else
begin
Dec(Size, FBufferSize);
I := 0;
while I <= Size do
begin
DoDecode(@Source[I], @Dest[I], FBufferSize);
Inc(I, FBufferSize);
end;
Dec(Size, I - FBufferSize);
if Size > 0 then
begin
if Size mod Context.BlockSize = 0 then
begin
DoDecode(@Source[I], @Dest[I], Size);
FState := csDecode;
end
else
begin
FState := csPadded;
ReportInvalidMessageLength(Self);
end;
end;
end;
end;
procedure TDECCipherModes.DecodeCFB8(Source, Dest: PByteArray; Size: Integer);
// CFB-8
var
I: Integer;
begin
I := 0;
while I < Size do
begin
DoEncode(FFeedback, FBuffer, FBufferSize);
Move(FFeedback[1], FFeedback[0], FBufferSize - 1);
FFeedback[FBufferSize - 1] := Source[I];
Dest[I] := Source[I] xor FBuffer[0];
Inc(I);
end;
FState := csDecode;
end;
procedure TDECCipherModes.DecodeOFB8(Source, Dest: PByteArray; Size: Integer);
// same as EncodeOFB
var
I: Integer;
begin
I := 0;
while I < Size do
begin
DoEncode(FFeedback, FBuffer, FBufferSize);
Move(FFeedback[1], FFeedback[0], FBufferSize - 1);
FFeedback[FBufferSize - 1] := FBuffer[0];
Dest[I] := Source[I] xor FBuffer[0];
Inc(I);
end;
FState := csDecode;
end;
procedure TDECCipherModes.DecodeCFS8(Source, Dest: PByteArray; Size: Integer);
var
I: Integer;
begin
I := 0;
while I < Size do
begin
DoEncode(FFeedback, FBuffer, FBufferSize);
Move(FFeedback[1], FFeedback[0], FBufferSize - 1);
FFeedback[FBufferSize - 1] := FFeedback[FBufferSize - 1] xor Source[I];
Dest[I] := Source[I] xor FBuffer[0];
Inc(I);
end;
FState := csDecode;
end;
procedure TDECCipherModes.DecodeCFBx(Source, Dest: PByteArray; Size: Integer);
// CFB-BlockSize
var
I: Integer;
F: PByteArray;
begin
FState := csDecode;
if FBufferIndex > 0 then
begin // remaining bytes of last decode
I := FBufferSize - FBufferIndex;
if I > Size then
I := Size;
Move(Source[0], FFeedback[FBufferIndex], I);
XORBuffers(Source[0], FBuffer[FBufferIndex], I, Dest[0]);
Inc(FBufferIndex, I);
if FBufferIndex < FBufferSize then
Exit;
Dec(Size, I);
Source := @Source[I];
Dest := @Dest[I];
FBufferIndex := 0
end;
// process chunks of FBufferSize bytes
Dec(Size, FBufferSize);
I := 0;
if Source <> Dest then
begin
F := FFeedback;
while I < Size do
begin
DoEncode(F, FBuffer, FBufferSize);
XORBuffers(Source[I], FBuffer[0], FBufferSize, Dest[I]);
F := @Source[I];
Inc(I, FBufferSize);
end;
if F <> FFeedback then
Move(F^, FFeedback^, FBufferSize);
end
else
while I < Size do
begin
DoEncode(FFeedback, FBuffer, FBufferSize);
Move(Source[I], FFeedback[0], FBufferSize);
XORBuffers(Source[I], FBuffer[0], FBufferSize, Dest[I]);
Inc(I, FBufferSize);
end;
Dec(Size, I - FBufferSize);
if Size > 0 then
begin // remaining bytes
DoEncode(FFeedback, FBuffer, FBufferSize);
Move(Source[I], FFeedback[0], Size);
XORBuffers(Source[I], FBuffer[0], Size, Dest[I]);
FBufferIndex := Size;
end;
end;
procedure TDECCipherModes.DecodeOFBx(Source, Dest: PByteArray; Size: Integer);
// OFB-BlockSize, same as EncodeOFBx
var
I: Integer;
begin
FState := csDecode;
if FBufferIndex > 0 then
begin
I := FBufferSize - FBufferIndex;
if I > Size then
I := Size;
XORBuffers(Source[0], FFeedback[FBufferIndex], I, Dest[0]);
Inc(FBufferIndex, I);
if FBufferIndex < FBufferSize then
Exit;
Dec(Size, I);
Source := @Source[I];
Dest := @Dest[I];
FBufferIndex := 0
end;
Dec(Size, FBufferSize);
I := 0;
while I < Size do
begin
DoEncode(FFeedback, FFeedback, FBufferSize);
XORBuffers(Source[I], FFeedback[0], FBufferSize, Dest[I]);
Inc(I, FBufferSize);
end;
Dec(Size, I - FBufferSize);
if Size > 0 then
begin
DoEncode(FFeedback, FFeedback, FBufferSize);
XORBuffers(Source[I], FFeedback[0], Size, Dest[I]);
FBufferIndex := Size;
end;
end;
procedure TDECCipherModes.DecodeCFSx(Source, Dest: PByteArray; Size: Integer);
// CFS-BlockSize
var
I: Integer;
begin
FState := csDecode;
if FBufferIndex > 0 then
begin // remaining bytes of last decode
I := FBufferSize - FBufferIndex;
if I > Size then
I := Size;
XORBuffers(Source[0], FFeedback[FBufferIndex], I, FFeedback[FBufferIndex]);
XORBuffers(Source[0], FBuffer[FBufferIndex], I, Dest[0]);
Inc(FBufferIndex, I);
if FBufferIndex < FBufferSize then
Exit;
Dec(Size, I);
Source := @Source[I];
Dest := @Dest[I];
FBufferIndex := 0
end;
// process chunks of FBufferSize bytes
Dec(Size, FBufferSize);
I := 0;
while I < Size do
begin
DoEncode(FFeedback, FBuffer, FBufferSize);
XORBuffers(Source[I], FFeedback[0], FBufferSize, FFeedback[0]);
XORBuffers(Source[I], FBuffer[0], FBufferSize, Dest[I]);
Inc(I, FBufferSize);
end;
Dec(Size, I - FBufferSize);
if Size > 0 then
begin // remaining bytes
DoEncode(FFeedback, FBuffer, FBufferSize);
XORBuffers(Source[I], FFeedback[0], Size, FFeedback[0]);
XORBuffers(Source[I], FBuffer[0], Size, Dest[I]);
FBufferIndex := Size;
end;
end;
procedure TDECCipherModes.DecodeCBCx(Source, Dest: PByteArray; Size: Integer);
var
I: Integer;
F, B, T: PByteArray;
begin
Dec(Size, FBufferSize);
F := FFeedback;
I := 0;
if Source = Dest then
begin
B := FBuffer;
while I <= Size do
begin
Move(Source[I], B[0], FBufferSize);
DoDecode(@Source[I], @Source[I], FBufferSize);
XORBuffers(Source[I], F[0], FBufferSize, Source[I]);
T := F;
F := B;
B := T;
Inc(I, FBufferSize);
end;
end
else
begin
while I <= Size do
begin
DoDecode(@Source[I], @Dest[I], FBufferSize);
XORBuffers(F[0], Dest[I], FBufferSize, Dest[I]);
F := @Source[I];
Inc(I, FBufferSize);
end;
end;
if F <> FFeedback then
Move(F[0], FFeedback[0], FBufferSize);
Dec(Size, I - FBufferSize);
if Size > 0 then
begin
DecodeCFB8(@Source[I], @Dest[I], Size);
FState := csPadded;
end
else
FState := csDecode;
end;
procedure TDECCipherModes.DecodeCTSx(Source, Dest: PByteArray; Size: Integer);
var
I: Integer;
F, B, T: PByteArray;
begin
Dec(Size, FBufferSize);
F := FFeedback;
B := FBuffer;
I := 0;
while I <= Size do
begin
XORBuffers(Source[I], F[0], FBufferSize, B[0]);
DoDecode(@Source[I], @Dest[I], FBufferSize);
XORBuffers(Dest[I], F[0], FBufferSize, Dest[I]);
T := B;
B := F;
F := T;
Inc(I, FBufferSize);
end;
if F <> FFeedback then
Move(F[0], FFeedback[0], FBufferSize);
Dec(Size, I - FBufferSize);
if Size > 0 then
begin
DecodeCFS8(@Source[I], @Dest[I], Size);
FState := csPadded;
end
else
FState := csDecode;
end;
{$IFDEF DEC3_CMCTS}
procedure DecodeCTS3(Source, Dest: PByteArray; Size: Integer);
var
I: Integer;
F, B, T: PByteArray;
begin
Dec(Size, FBufferSize);
F := FFeedback;
B := FBuffer;
I := 0;
while I <= Size do
begin
XORBuffers(Source[I], F[0], FBufferSize, B[0]);
DoDecode(@Source[I], @Dest[I], FBufferSize);
XORBuffers(Dest[I], F[0], FBufferSize, Dest[I]);
T := B;
B := F;
F := T;
Inc(I, FBufferSize);
end;
if F <> FFeedback then
Move(F[0], FFeedback[0], FBufferSize);
Dec(Size, I - FBufferSize);
if Size > 0 then
begin
DecodeCFSx(@Source[I], @Dest[I], Size); // use the padding implemented in CFSx
FState := csPadded;
end
else
FState := csDecode;
end;
{$ENDIF DEC3_CMCTS}
end.