1298 lines
44 KiB
ObjectPascal
1298 lines
44 KiB
ObjectPascal
{******************************************************************************}
|
|
{ }
|
|
{ Library: Fundamentals 5.00 }
|
|
{ File name: flcCipher.pas }
|
|
{ File version: 5.11 }
|
|
{ Description: Cipher library }
|
|
{ }
|
|
{ Copyright: Copyright (c) 2007-2020, David J Butler }
|
|
{ All rights reserved. }
|
|
{ This file is licensed under the BSD License. }
|
|
{ See http://www.opensource.org/licenses/bsd-license.php }
|
|
{ Redistribution and use in source and binary forms, with }
|
|
{ or without modification, are permitted provided that }
|
|
{ the following conditions are met: }
|
|
{ Redistributions of source code must retain the above }
|
|
{ copyright notice, this list of conditions and the }
|
|
{ following disclaimer. }
|
|
{ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND }
|
|
{ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED }
|
|
{ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED }
|
|
{ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A }
|
|
{ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL }
|
|
{ THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, }
|
|
{ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR }
|
|
{ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
|
|
{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF }
|
|
{ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) }
|
|
{ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER }
|
|
{ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING }
|
|
{ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE }
|
|
{ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE }
|
|
{ POSSIBILITY OF SUCH DAMAGE. }
|
|
{ }
|
|
{ Github: https://github.com/fundamentalslib }
|
|
{ E-mail: fundamentals.library at gmail.com }
|
|
{ }
|
|
{ Revision history: }
|
|
{ }
|
|
{ 2007/01/05 0.01 Initial version }
|
|
{ 2007/01/07 0.02 ECB and padding support }
|
|
{ 2008/06/15 0.03 CBC mode }
|
|
{ 2008/06/17 0.04 CFB and OFB modes }
|
|
{ 2010/11/18 4.05 Revision }
|
|
{ 2010/12/16 4.06 AES cipher }
|
|
{ 2011/08/09 4.07 Revision }
|
|
{ 2015/03/21 4.08 Revision }
|
|
{ 2016/01/09 5.09 Revised for Fundamentals 5. }
|
|
{ 2018/06/17 5.10 Type changes. }
|
|
{ 2019/06/09 5.11 Tests for Triple-DES-EDE-3. }
|
|
{ }
|
|
{ Supported compilers: }
|
|
{ }
|
|
{ Delphi 5 Win32 4.08 2015/03/31 }
|
|
{ Delphi 7 Win32 5.10 2019/02/24 }
|
|
{ Delphi XE7 Win32 5.09 2016/01/09 }
|
|
{ Delphi XE7 Win64 5.09 2016/01/09 }
|
|
{ Delphi 10 Win32 5.09 2016/01/09 }
|
|
{ Delphi 10 Win64 5.09 2016/01/09 }
|
|
{ }
|
|
{ Todo: }
|
|
{ - Split out tests }
|
|
{******************************************************************************}
|
|
|
|
{.DEFINE Cipher_SupportRSA}
|
|
|
|
{$INCLUDE flcCipher.inc}
|
|
|
|
unit flcCipher;
|
|
|
|
interface
|
|
|
|
uses
|
|
{ System }
|
|
SysUtils,
|
|
{ Fundamentals }
|
|
flcStdTypes,
|
|
flcUtils,
|
|
{ Cipher }
|
|
flcCipherUtils,
|
|
flcCipherRC2,
|
|
flcCipherRC4,
|
|
flcCipherDES,
|
|
flcCipherAES
|
|
{$IFDEF Cipher_SupportRSA},
|
|
flcCipherRSA
|
|
{$ENDIF};
|
|
|
|
|
|
|
|
{ }
|
|
{ Cipher information }
|
|
{ }
|
|
type
|
|
TCipherType = (
|
|
ctNone,
|
|
ctRC2, // RC2
|
|
ctRC4, // RC4
|
|
ctDES, // DES
|
|
ctDES40, // DES-40
|
|
ctTripleDESEDE, // Triple-DES-EDE 2 keys
|
|
ctTripleDES3EDE, // Triple-DES-EDE 3 keys
|
|
ctTripleDES3EEE, // Triple-DES-EEE 3 keys
|
|
ctAES, // AES-128, AES-192, AES-256
|
|
ctRSAOAEP1024 // RSA-OAEP-1024
|
|
);
|
|
|
|
TCipherOperation = (
|
|
coEncrypt,
|
|
coDecrypt);
|
|
|
|
TCipherInitFunc =
|
|
procedure (const Operation: TCipherOperation;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const KeyBits: Integer;
|
|
const ContextBuffer: Pointer);
|
|
|
|
TCipherBufferFunc =
|
|
procedure (const Context, Data: Pointer; const DataSize: Integer);
|
|
|
|
TCipherFinaliseFunc =
|
|
procedure (const Context: Pointer);
|
|
|
|
TCipherBufferMode = (
|
|
cbmStream,
|
|
cbmMultipleBlocks,
|
|
cbmSingleBlock);
|
|
|
|
TCipherInfo = record
|
|
ValidCipher : Boolean;
|
|
ShortName : RawByteString;
|
|
BufferMode : TCipherBufferMode;
|
|
InBlockSize : Integer;
|
|
OutBlockSize : Integer;
|
|
KeyBitsMin : Integer;
|
|
KeyBitsMax : Integer;
|
|
ContextSize : Integer;
|
|
InitFunc : TCipherInitFunc;
|
|
EncryptFunc : TCipherBufferFunc;
|
|
DecryptFunc : TCipherBufferFunc;
|
|
FinaliseFunc : TCipherFinaliseFunc;
|
|
end;
|
|
PCipherInfo = ^TCipherInfo;
|
|
|
|
function GetCipherInfo(const Cipher: TCipherType): PCipherInfo;
|
|
|
|
|
|
|
|
{ }
|
|
{ Cipher }
|
|
{ }
|
|
type
|
|
TCipherMode = (
|
|
cmECB, // Electronic Codebook
|
|
cmCBC, // Cipher Block Chaining
|
|
cmCFB, // Cipher Feedback
|
|
cmOFB // Output Feedback
|
|
);
|
|
|
|
TCipherPadding = (
|
|
cpNone, // Padded with zeros (size of decrypted text may be longer
|
|
// than original text if original text size is not a
|
|
// multiple of cipher's block size; used with test cases)
|
|
cpPadSizeByte, // Padded with zeros and pad size byte
|
|
cpPadPKCS5); // Padded with pad size byte repeated
|
|
|
|
const
|
|
MaxCipherContextSize = 384;
|
|
|
|
type
|
|
TCipherState = record
|
|
Operation : TCipherOperation;
|
|
Cipher : TCipherType;
|
|
Mode : TCipherMode;
|
|
Padding : TCipherPadding;
|
|
KeyBits : Integer;
|
|
CipherInfo : PCipherInfo;
|
|
Context : array[0..MaxCipherContextSize - 1] of Byte;
|
|
end;
|
|
|
|
const
|
|
CipherStateSize = SizeOf(TCipherState);
|
|
|
|
|
|
|
|
{ }
|
|
{ Encrypt }
|
|
{ }
|
|
procedure EncryptInit(
|
|
var State: TCipherState;
|
|
const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding;
|
|
const KeyBits: Integer;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer);
|
|
|
|
function EncryptBuf(
|
|
var State: TCipherState;
|
|
const InputBuffer: Pointer; const InputBufferSize: Integer;
|
|
const OutputBuffer: Pointer; const OutputBufferSize: Integer;
|
|
const InitVectorBuffer: Pointer; const InitVectorBufferSize: Integer): Integer;
|
|
|
|
procedure EncryptFinalise(var State: TCipherState);
|
|
|
|
function EncryptedSize(
|
|
const Cipher: TCipherType; const Padding: TCipherPadding;
|
|
const BufferSize: Integer): Integer;
|
|
|
|
function Encrypt(
|
|
const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding;
|
|
const KeyBits: Integer;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const InputBuffer: Pointer; const InputBufferSize: Integer;
|
|
const OutputBuffer: Pointer; const OutputBufferSize: Integer;
|
|
const InitVectorBuffer: Pointer; const InitVectorBufferSize: Integer): Integer; overload;
|
|
|
|
function Encrypt(
|
|
const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding;
|
|
const KeyBits: Integer;
|
|
const Key, Data, InitVector: RawByteString): RawByteString; overload;
|
|
|
|
|
|
|
|
{ }
|
|
{ Decrypt }
|
|
{ }
|
|
procedure DecryptInit(
|
|
var State: TCipherState;
|
|
const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding;
|
|
const KeyBits: Integer;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer);
|
|
|
|
function DecryptBuf(
|
|
var State: TCipherState;
|
|
const Buffer: Pointer; const BufferSize: Integer;
|
|
const InitVectorBuffer: Pointer; const InitVectorBufferSize: Integer): Integer;
|
|
|
|
procedure DecryptFinalise(var State: TCipherState);
|
|
|
|
function Decrypt(
|
|
const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding;
|
|
const KeyBits: Integer;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const Buffer: Pointer; const BufferSize: Integer;
|
|
const InitVectorBuffer: Pointer; const InitVectorBufferSize: Integer): Integer; overload;
|
|
|
|
function Decrypt(
|
|
const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding;
|
|
const KeyBits: Integer;
|
|
const Key, Data, InitVector: RawByteString): RawByteString; overload;
|
|
|
|
|
|
|
|
{ }
|
|
{ Test cases }
|
|
{ }
|
|
{$IFDEF CIPHER_TEST}
|
|
procedure Test;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
{ Fundamentals }
|
|
flcHugeInt,
|
|
|
|
{ Cipher }
|
|
flcCipherRandom;
|
|
|
|
|
|
|
|
{ }
|
|
{ RC2 cipher }
|
|
{ }
|
|
const
|
|
RC2ContextSize = Sizeof(TRC2CipherKey);
|
|
|
|
procedure CipherRC2Init(const Operation: TCipherOperation;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const KeyBits: Integer; const ContextBuffer: Pointer);
|
|
begin
|
|
RC2Init(KeyBuffer^, KeyBufferSize, KeyBits, PRC2CipherKey(ContextBuffer)^);
|
|
end;
|
|
|
|
procedure CipherRC2EncryptBuffer(const Context, Data: Pointer; const DataSize: Integer);
|
|
begin
|
|
Assert(DataSize = RC2BlockSize);
|
|
RC2EncryptBlock(PRC2CipherKey(Context)^, PRC2Block(Data)^);
|
|
end;
|
|
|
|
procedure CipherRC2DecryptBuffer(const Context, Data: Pointer; const DataSize: Integer);
|
|
begin
|
|
Assert(DataSize = RC2BlockSize);
|
|
RC2DecryptBlock(PRC2CipherKey(Context)^, PRC2Block(Data)^);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ RC4 cipher }
|
|
{ }
|
|
const
|
|
RC4ContextSize = Sizeof(TRC4Context);
|
|
|
|
procedure CipherRC4Init(const Operation: TCipherOperation;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const KeyBits: Integer; const ContextBuffer: Pointer);
|
|
begin
|
|
RC4Init(KeyBuffer^, KeyBufferSize, PRC4Context(ContextBuffer)^);
|
|
end;
|
|
|
|
procedure CipherRC4Buffer(const Context, Data: Pointer; const DataSize: Integer);
|
|
begin
|
|
RC4Buffer(PRC4Context(Context)^, Data^, DataSize);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ DES cipher }
|
|
{ }
|
|
const
|
|
DESContextSize = Sizeof(TDESContext);
|
|
DESKeySize = Sizeof(TDESKey);
|
|
|
|
procedure CipherDESInit(const Operation: TCipherOperation;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const KeyBits: Integer; const ContextBuffer: Pointer);
|
|
begin
|
|
if KeyBufferSize <> DESKeySize then
|
|
raise ECipher.Create(CipherError_InvalidKeySize, 'Invalid DES key length');
|
|
DESInit(Operation = coEncrypt, PDESKey(KeyBuffer)^, PDESContext(ContextBuffer)^);
|
|
end;
|
|
|
|
procedure CipherDESBuffer(const Context, Data: Pointer; const DataSize: Integer);
|
|
begin
|
|
Assert(DataSize = DESBlockSize);
|
|
DESBuffer(PDESContext(Context)^, PDESBlock(Data)^);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ DES-40 cipher }
|
|
{ }
|
|
const
|
|
DES40ContextSize = DESContextSize;
|
|
DES40KeySize = DESKeySize;
|
|
|
|
procedure CipherDES40Init(const Operation: TCipherOperation;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const KeyBits: Integer; const ContextBuffer: Pointer);
|
|
var DES40Key : TDESKey;
|
|
begin
|
|
if KeyBufferSize <> DES40KeySize then
|
|
raise ECipher.Create(CipherError_InvalidKeySize, 'Invalid DES key length');
|
|
Move(KeyBuffer, DES40Key, DES40KeySize);
|
|
DESKeyConvertToDES40(DES40Key);
|
|
DESInit(Operation = coEncrypt, DES40Key, PDESContext(ContextBuffer)^);
|
|
end;
|
|
|
|
procedure CipherDES40Buffer(const Context, Data: Pointer; const DataSize: Integer);
|
|
begin
|
|
Assert(DataSize = DESBlockSize);
|
|
DESBuffer(PDESContext(Context)^, PDESBlock(Data)^);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Triple-DES cipher }
|
|
{ }
|
|
const
|
|
TripleDESContextSize = Sizeof(TTripleDESContext);
|
|
|
|
procedure CipherTripleDESInit(const Operation: TCipherOperation;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const KeyBits: Integer; const ContextBuffer: Pointer);
|
|
begin
|
|
if KeyBufferSize <> SizeOf(TTripleDESKey) then
|
|
raise ECipher.Create(CipherError_InvalidKeySize, 'Invalid DES key length');
|
|
TripleDESInit(Operation = coEncrypt, PTripleDESKey(KeyBuffer)^,
|
|
PTripleDESContext(ContextBuffer)^);
|
|
end;
|
|
|
|
procedure CipherTripleDESBuffer(const Context, Data: Pointer; const DataSize: Integer);
|
|
begin
|
|
Assert(DataSize = DESBlockSize);
|
|
TripleDESBuffer(PTripleDESContext(Context)^, PDESBlock(Data)^);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Triple-DES-3 cipher }
|
|
{ }
|
|
const
|
|
TripleDES3ContextSize = Sizeof(TTripleDES3Context);
|
|
|
|
procedure CipherTripleDES3EDEInit(const Operation: TCipherOperation;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const KeyBits: Integer; const ContextBuffer: Pointer);
|
|
begin
|
|
if KeyBufferSize <> SizeOf(TTripleDES3Key) then
|
|
raise ECipher.Create(CipherError_InvalidKeySize, 'Invalid DES key length');
|
|
TripleDES3Init(Operation = coEncrypt, PTripleDES3Key(KeyBuffer)^, False,
|
|
PTripleDES3Context(ContextBuffer)^);
|
|
end;
|
|
|
|
procedure CipherTripleDES3EEEInit(const Operation: TCipherOperation;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const KeyBits: Integer; const ContextBuffer: Pointer);
|
|
begin
|
|
if KeyBufferSize <> SizeOf(TTripleDES3Key) then
|
|
raise ECipher.Create(CipherError_InvalidKeySize, 'Invalid DES key length');
|
|
TripleDES3Init(Operation = coEncrypt, PTripleDES3Key(KeyBuffer)^, True,
|
|
PTripleDES3Context(ContextBuffer)^);
|
|
end;
|
|
|
|
procedure CipherTripleDES3BufferEncrypt(const Context, Data: Pointer; const DataSize: Integer);
|
|
begin
|
|
Assert(DataSize = DESBlockSize);
|
|
TripleDES3BufferEncrypt(PTripleDES3Context(Context)^, PDESBlock(Data)^);
|
|
end;
|
|
|
|
procedure CipherTripleDES3BufferDecrypt(const Context, Data: Pointer; const DataSize: Integer);
|
|
begin
|
|
Assert(DataSize = DESBlockSize);
|
|
TripleDES3BufferDecrypt(PTripleDES3Context(Context)^, PDESBlock(Data)^);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ AES cipher }
|
|
{ }
|
|
const
|
|
AESContextSize = Sizeof(TAESContext);
|
|
|
|
procedure CipherAESInit(const Operation: TCipherOperation;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const KeyBits: Integer; const ContextBuffer: Pointer);
|
|
begin
|
|
AESInit(PAESContext(ContextBuffer)^, KeyBits, KeyBuffer^, KeyBufferSize);
|
|
end;
|
|
|
|
procedure CipherAESEncryptBuffer(const Context, Data: Pointer; const DataSize: Integer);
|
|
begin
|
|
Assert(DataSize = AESBlockSize);
|
|
AESEncryptBlock(PAESContext(Context)^, Data^, DataSize, Data^, DataSize);
|
|
end;
|
|
|
|
procedure CipherAESDecryptBuffer(const Context, Data: Pointer; const DataSize: Integer);
|
|
begin
|
|
Assert(DataSize = AESBlockSize);
|
|
AESDecryptBlock(PAESContext(Context)^, Data^, DataSize, Data^, DataSize);
|
|
end;
|
|
|
|
procedure CipherAESFinalise(const Context: Pointer);
|
|
begin
|
|
AESFinalise(PAESContext(Context)^);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ RSA cipher }
|
|
{ Note: Stub under development }
|
|
{ }
|
|
{$IFDEF Cipher_SupportRSA}
|
|
type
|
|
TRSAOAEP1024Context = packed record
|
|
Encrypt : Boolean;
|
|
PublicKey : TRSAPublicKey;
|
|
PrivateKey : TRSAPrivateKey;
|
|
end;
|
|
PRSAOAEP1024Context = ^TRSAOAEP1024Context;
|
|
|
|
const
|
|
RSAOAEP1024ContextSize = SizeOf(TRSAOAEP1024Context);
|
|
|
|
procedure CipherRSAOAEP1024Init(const Operation: TCipherOperation;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const KeyBits: Integer; const ContextBuffer: Pointer);
|
|
const
|
|
KeyPartBits = 1024;
|
|
KeyPartSize = 1024 div 8;
|
|
var C : PRSAOAEP1024Context;
|
|
P, Q : PByte;
|
|
begin
|
|
C := ContextBuffer;
|
|
C^.Encrypt := Operation = coEncrypt;
|
|
RSAPublicKeyInit(C^.PublicKey);
|
|
RSAPrivateKeyInit(C^.PrivateKey);
|
|
if (KeyBufferSize <> KeyPartSize * 2) or (KeyBits <> KeyPartBits) then
|
|
raise ECipher.Create(CipherError_InvalidKeySize, 'Invalid RSA key length');
|
|
P := @KeyBuffer;
|
|
Q := P;
|
|
Inc(Q, KeyPartSize);
|
|
if Operation = coEncrypt then
|
|
RSAPublicKeyAssignBuf(C^.PublicKey, KeyPartBits, P^, KeyPartSize, Q^, KeyPartSize, False)
|
|
else
|
|
RSAPrivateKeyAssignBuf(C^.PrivateKey, KeyPartBits, P^, KeyPartSize, Q^, KeyPartSize, False);
|
|
end;
|
|
|
|
procedure CipherRSAOAEP1024EncryptBuffer(const Context, Data: Pointer; const DataSize: Integer);
|
|
var C : PRSAOAEP1024Context;
|
|
B : array[0..127] of Byte;
|
|
begin
|
|
C := Context;
|
|
RSAEncrypt(rsaetOAEP, C^.PublicKey, Data, DataSize, B[0], SizeOf(B));
|
|
Move(B[0], Data^, 128);
|
|
end;
|
|
|
|
procedure CipherRSAOAEP1024DecryptBuffer(const Context, Data: Pointer; const DataSize: Integer);
|
|
var C : PRSAOAEP1024Context;
|
|
B : array[0..127] of Byte;
|
|
begin
|
|
C := Context;
|
|
RSADecrypt(rsaetOAEP, C^.PrivateKey, Data, DataSize, B[0], SizeOf(B));
|
|
Move(B[0], Data^, 128);
|
|
end;
|
|
|
|
procedure CipherRSAOAEP1024Finalise(const Context: Pointer);
|
|
var C : PRSAOAEP1024Context;
|
|
begin
|
|
C := Context;
|
|
RSAPrivateKeyFinalise(C^.PrivateKey);
|
|
RSAPublicKeyFinalise(C^.PublicKey);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ Cipher mode buffer functions }
|
|
{ }
|
|
procedure XORBuffer(const Buffer1, Buffer2: Pointer; const BufferSize: Integer);
|
|
var I : Integer;
|
|
P, Q : PByte;
|
|
begin
|
|
Assert(Assigned(Buffer1));
|
|
Assert(Assigned(Buffer2));
|
|
P := Buffer1;
|
|
Q := Buffer2;
|
|
for I := 0 to BufferSize - 1 do
|
|
begin
|
|
P^ := P^ xor Q^;
|
|
Inc(P);
|
|
Inc(Q);
|
|
end;
|
|
end;
|
|
|
|
{ Cipher buffer }
|
|
procedure CipherBuffer(
|
|
const Buffer: Pointer; const BufferSize: Integer;
|
|
const CipherContext: Pointer;
|
|
const CipherFunc: TCipherBufferFunc; const CipherBlockSize: Integer);
|
|
begin
|
|
CipherFunc(CipherContext, @Buffer, BufferSize);
|
|
end;
|
|
|
|
{ ECB - Electronic codebook }
|
|
procedure ECBBuffer(
|
|
const Buffer: Pointer; const BufferSize: Integer;
|
|
const CipherContext: Pointer;
|
|
const CipherFunc: TCipherBufferFunc; const CipherBlockSize: Integer);
|
|
var P : PByte;
|
|
L, M : Integer;
|
|
begin
|
|
P := Buffer;
|
|
L := BufferSize;
|
|
while L > 0 do
|
|
begin
|
|
if L >= CipherBlockSize then
|
|
M := CipherBlockSize
|
|
else
|
|
M := L;
|
|
CipherFunc(CipherContext, P, M);
|
|
Inc(P, M);
|
|
Dec(L, M);
|
|
end;
|
|
end;
|
|
|
|
{ CBC - Cipher-block chaining }
|
|
{ See http://en.wikipedia.org/wiki/Cipher_block_chaining }
|
|
const
|
|
MaxCipherBlockSize = 4096;
|
|
|
|
procedure CBCEncode(
|
|
const Buffer: Pointer; const BufferSize: Integer;
|
|
const CipherContext: Pointer;
|
|
const CipherFunc: TCipherBufferFunc; const CipherBlockSize: Integer;
|
|
const InitVector: Pointer; const InitVectorBufferSize: Integer);
|
|
var P, F : PByte;
|
|
L : Integer;
|
|
B : array[0..MaxCipherBlockSize - 1] of Byte;
|
|
begin
|
|
Assert(BufferSize > 0);
|
|
Assert(CipherBlockSize <= MaxCipherBlockSize);
|
|
Assert(Assigned(InitVector));
|
|
Assert(InitVectorBufferSize = CipherBlockSize);
|
|
P := Buffer;
|
|
L := BufferSize;
|
|
F := InitVector;
|
|
while L >= CipherBlockSize do
|
|
begin
|
|
XORBuffer(P, F, CipherBlockSize);
|
|
CipherFunc(CipherContext, P, CipherBlockSize);
|
|
F := P;
|
|
Dec(L, CipherBlockSize);
|
|
Inc(P, CipherBlockSize);
|
|
end;
|
|
if L > 0 then
|
|
begin
|
|
Move(P^, B[0], L);
|
|
FillChar(B[L], CipherBlockSize - L, 0);
|
|
XORBuffer(@B[0], F, CipherBlockSize);
|
|
CipherFunc(CipherContext, @B[0], L);
|
|
Move(B[0], P^, L);
|
|
SecureClear(B[0], CipherBlockSize);
|
|
end;
|
|
end;
|
|
|
|
procedure CBCDecode(
|
|
const Buffer: Pointer; const BufferSize: Integer;
|
|
const CipherState: Pointer;
|
|
const CipherFunc: TCipherBufferFunc; const CipherBlockSize: Integer;
|
|
const InitVector: Pointer; const InitVectorBufferSize: Integer);
|
|
var P : PByte;
|
|
L : Integer;
|
|
B : array[0..MaxCipherBlockSize - 1] of Byte;
|
|
C : array[0..MaxCipherBlockSize - 1] of Byte;
|
|
begin
|
|
Assert(BufferSize > 0);
|
|
Assert(CipherBlockSize <= MaxCipherBlockSize);
|
|
Assert(Assigned(InitVector));
|
|
Assert(InitVectorBufferSize = CipherBlockSize);
|
|
P := Buffer;
|
|
L := BufferSize;
|
|
Move(InitVector^, B[0], CipherBlockSize);
|
|
while L >= CipherBlockSize do
|
|
begin
|
|
Move(P^, C[0], CipherBlockSize);
|
|
CipherFunc(CipherState, P, CipherBlockSize);
|
|
XORBuffer(P, @B[0], CipherBlockSize);
|
|
Move(C[0], B[0], CipherBlockSize);
|
|
Dec(L, CipherBlockSize);
|
|
Inc(P, CipherBlockSize);
|
|
end;
|
|
if L > 0 then
|
|
begin
|
|
Move(P^, C[0], L);
|
|
FillChar(C[L], CipherBlockSize - L, 0);
|
|
CipherFunc(CipherState, @C[0], L);
|
|
XORBuffer(@C[0], @B[0], CipherBlockSize);
|
|
Move(C[0], P^, L);
|
|
end;
|
|
SecureClear(B[0], CipherBlockSize);
|
|
SecureClear(C[0], CipherBlockSize);
|
|
end;
|
|
|
|
{ CFB - Cipher FeedBack }
|
|
procedure CFBEncode(
|
|
const Buffer: Pointer; const BufferSize: Integer;
|
|
const CipherContext: Pointer;
|
|
const CipherFunc: TCipherBufferFunc; const CipherBlockSize: Integer;
|
|
const InitVector: Pointer; const InitVectorBufferSize: Integer);
|
|
var P : PByte;
|
|
L : Integer;
|
|
B, F : array[0..MaxCipherBlockSize - 1] of Byte;
|
|
begin
|
|
Assert(BufferSize > 0);
|
|
Assert(CipherBlockSize <= MaxCipherBlockSize);
|
|
Assert(Assigned(InitVector));
|
|
Assert(InitVectorBufferSize = CipherBlockSize);
|
|
P := Buffer;
|
|
L := BufferSize;
|
|
Move(InitVector^, F[0], CipherBlockSize);
|
|
while L >= 0 do
|
|
begin
|
|
Move(F[0], B[0], CipherBlockSize);
|
|
CipherFunc(CipherContext, @B[0], CipherBlockSize);
|
|
P^ := P^ xor B[0];
|
|
Move(F[1], F[0], CipherBlockSize - 1);
|
|
F[CipherBlockSize - 1] := P^;
|
|
Dec(L);
|
|
Inc(P);
|
|
end;
|
|
SecureClear(B[0], CipherBlockSize);
|
|
end;
|
|
|
|
procedure CFBDecode(
|
|
const Buffer: Pointer; const BufferSize: Integer;
|
|
const CipherContext: Pointer;
|
|
const CipherFunc: TCipherBufferFunc; const CipherBlockSize: Integer;
|
|
const InitVector: Pointer; const InitVectorBufferSize: Integer);
|
|
var P : PByte;
|
|
C : Byte;
|
|
L : Integer;
|
|
B, F : array[0..MaxCipherBlockSize - 1] of Byte;
|
|
begin
|
|
Assert(BufferSize > 0);
|
|
Assert(CipherBlockSize <= MaxCipherBlockSize);
|
|
Assert(Assigned(InitVector));
|
|
Assert(InitVectorBufferSize = CipherBlockSize);
|
|
P := Buffer;
|
|
L := BufferSize;
|
|
Move(InitVector^, F[0], CipherBlockSize);
|
|
while L >= 0 do
|
|
begin
|
|
Move(F[0], B[0], CipherBlockSize);
|
|
CipherFunc(CipherContext, @B[0], CipherBlockSize);
|
|
Move(F[1], F[0], CipherBlockSize - 1);
|
|
C := P^;
|
|
F[CipherBlockSize - 1] := C;
|
|
P^ := C xor B[0];
|
|
Dec(L);
|
|
Inc(P);
|
|
end;
|
|
SecureClear(B[0], CipherBlockSize);
|
|
end;
|
|
|
|
{ OFB - Output FeedBack }
|
|
procedure OFBEncode(
|
|
const Buffer: Pointer; const BufferSize: Integer;
|
|
const CipherContext: Pointer;
|
|
const CipherFunc: TCipherBufferFunc; const CipherBlockSize: Integer;
|
|
const InitVector: Pointer; const InitVectorBufferSize: Integer);
|
|
var P : PByte;
|
|
L : Integer;
|
|
B, F : array[0..MaxCipherBlockSize - 1] of Byte;
|
|
begin
|
|
Assert(BufferSize > 0);
|
|
Assert(CipherBlockSize <= MaxCipherBlockSize);
|
|
Assert(Assigned(InitVector));
|
|
Assert(InitVectorBufferSize = CipherBlockSize);
|
|
P := Buffer;
|
|
L := BufferSize;
|
|
Move(InitVector^, F[0], CipherBlockSize);
|
|
while L >= 0 do
|
|
begin
|
|
Move(F[0], B[0], CipherBlockSize);
|
|
CipherFunc(CipherContext, @B[0], CipherBlockSize);
|
|
P^ := P^ xor B[0];
|
|
Move(F[1], F[0], CipherBlockSize - 1);
|
|
F[CipherBlockSize - 1] := B[0];
|
|
Dec(L);
|
|
Inc(P);
|
|
end;
|
|
SecureClear(B[0], CipherBlockSize);
|
|
end;
|
|
|
|
procedure OFBDecode(
|
|
const Buffer: Pointer; const BufferSize: Integer;
|
|
const CipherContext: Pointer;
|
|
const CipherFunc: TCipherBufferFunc; const CipherBlockSize: Integer;
|
|
const InitVector: Pointer; const InitVectorBufferSize: Integer);
|
|
var P : PByte;
|
|
L : Integer;
|
|
B, F : array[0..MaxCipherBlockSize - 1] of Byte;
|
|
begin
|
|
Assert(BufferSize > 0);
|
|
Assert(CipherBlockSize <= MaxCipherBlockSize);
|
|
Assert(Assigned(InitVector));
|
|
Assert(InitVectorBufferSize = CipherBlockSize);
|
|
P := Buffer;
|
|
L := BufferSize;
|
|
Move(InitVector^, F[0], CipherBlockSize);
|
|
while L >= 0 do
|
|
begin
|
|
Move(F[0], B[0], CipherBlockSize);
|
|
CipherFunc(CipherContext, @B[0], CipherBlockSize);
|
|
Move(F[1], F[0], CipherBlockSize - 1);
|
|
F[CipherBlockSize - 1] := B[0];
|
|
P^ := P^ xor B[0];
|
|
Dec(L);
|
|
Inc(P);
|
|
end;
|
|
SecureClear(B[0], CipherBlockSize);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Cipher information }
|
|
{ }
|
|
const
|
|
CipherInfo : array[TCipherType] of TCipherInfo = (
|
|
(
|
|
ValidCipher: False;
|
|
ShortName: '';
|
|
BufferMode: cbmStream;
|
|
InBlockSize: 0;
|
|
OutBlockSize: 0;
|
|
KeyBitsMin: 0;
|
|
KeyBitsMax: 0;
|
|
ContextSize: 0;
|
|
InitFunc: nil;
|
|
EncryptFunc: nil;
|
|
DecryptFunc: nil;
|
|
FinaliseFunc: nil;
|
|
),
|
|
|
|
(
|
|
ValidCipher: True;
|
|
ShortName: 'RC2';
|
|
BufferMode: cbmMultipleBlocks;
|
|
InBlockSize: RC2BlockSize;
|
|
OutBlockSize: RC2BlockSize;
|
|
KeyBitsMin: 1;
|
|
KeyBitsMax: 1024;
|
|
ContextSize: RC2ContextSize;
|
|
InitFunc: CipherRC2Init;
|
|
EncryptFunc: CipherRC2EncryptBuffer;
|
|
DecryptFunc: CipherRC2DecryptBuffer;
|
|
FinaliseFunc: nil;
|
|
),
|
|
|
|
(
|
|
ValidCipher: True;
|
|
ShortName: 'RC4';
|
|
BufferMode: cbmStream;
|
|
InBlockSize: 1024;
|
|
OutBlockSize: 1024;
|
|
KeyBitsMin: 1;
|
|
KeyBitsMax: 2048;
|
|
ContextSize: RC4ContextSize;
|
|
InitFunc: CipherRC4Init;
|
|
EncryptFunc: CipherRC4Buffer;
|
|
DecryptFunc: CipherRC4Buffer;
|
|
FinaliseFunc: nil;
|
|
),
|
|
|
|
(
|
|
ValidCipher: True;
|
|
ShortName: 'DES';
|
|
BufferMode: cbmMultipleBlocks;
|
|
InBlockSize: DESBlockSize;
|
|
OutBlockSize: DESBlockSize;
|
|
KeyBitsMin: 64;
|
|
KeyBitsMax: 64;
|
|
ContextSize: DESContextSize;
|
|
InitFunc: CipherDESInit;
|
|
EncryptFunc: CipherDESBuffer;
|
|
DecryptFunc: CipherDESBuffer;
|
|
FinaliseFunc: nil;
|
|
),
|
|
|
|
(
|
|
ValidCipher: True;
|
|
ShortName: 'DES-40';
|
|
BufferMode: cbmMultipleBlocks;
|
|
InBlockSize: DESBlockSize;
|
|
OutBlockSize: DESBlockSize;
|
|
KeyBitsMin: 64;
|
|
KeyBitsMax: 64;
|
|
ContextSize: DES40ContextSize;
|
|
InitFunc: CipherDES40Init;
|
|
EncryptFunc: CipherDES40Buffer;
|
|
DecryptFunc: CipherDES40Buffer;
|
|
FinaliseFunc: nil;
|
|
),
|
|
|
|
(
|
|
ValidCipher: True;
|
|
ShortName: 'TripleDES';
|
|
BufferMode: cbmMultipleBlocks;
|
|
InBlockSize: DESBlockSize;
|
|
OutBlockSize: DESBlockSize;
|
|
KeyBitsMin: 128;
|
|
KeyBitsMax: 128;
|
|
ContextSize: TripleDESContextSize;
|
|
InitFunc: CipherTripleDESInit;
|
|
EncryptFunc: CipherTripleDESBuffer;
|
|
DecryptFunc: CipherTripleDESBuffer;
|
|
FinaliseFunc: nil;
|
|
),
|
|
|
|
(
|
|
ValidCipher: True;
|
|
ShortName: 'TripleDES3EDE';
|
|
BufferMode: cbmMultipleBlocks;
|
|
InBlockSize: DESBlockSize;
|
|
OutBlockSize: DESBlockSize;
|
|
KeyBitsMin: 192;
|
|
KeyBitsMax: 192;
|
|
ContextSize: TripleDES3ContextSize;
|
|
InitFunc: CipherTripleDES3EDEInit;
|
|
EncryptFunc: CipherTripleDES3BufferEncrypt;
|
|
DecryptFunc: CipherTripleDES3BufferDecrypt;
|
|
FinaliseFunc: nil;
|
|
),
|
|
|
|
(
|
|
ValidCipher: True;
|
|
ShortName: 'TipleDES3EEE';
|
|
BufferMode: cbmMultipleBlocks;
|
|
InBlockSize: DESBlockSize;
|
|
OutBlockSize: DESBlockSize;
|
|
KeyBitsMin: 192;
|
|
KeyBitsMax: 192;
|
|
ContextSize: TripleDES3ContextSize;
|
|
InitFunc: CipherTripleDES3EEEInit;
|
|
EncryptFunc: CipherTripleDES3BufferEncrypt;
|
|
DecryptFunc: CipherTripleDES3BufferDecrypt;
|
|
FinaliseFunc: nil;
|
|
),
|
|
|
|
(
|
|
ValidCipher: True;
|
|
ShortName: 'AES';
|
|
BufferMode: cbmMultipleBlocks;
|
|
InBlockSize: AESBlockSize;
|
|
OutBlockSize: AESBlockSize;
|
|
KeyBitsMin: 128;
|
|
KeyBitsMax: 256;
|
|
ContextSize: AESContextSize;
|
|
InitFunc: CipherAESInit;
|
|
EncryptFunc: CipherAESEncryptBuffer;
|
|
DecryptFunc: CipherAESDecryptBuffer;
|
|
FinaliseFunc: CipherAESFinalise;
|
|
),
|
|
|
|
{$IFDEF Cipher_SupportRSA}
|
|
(
|
|
ValidCipher: True;
|
|
ShortName: 'RSA-OAEP-1024';
|
|
BufferMode: cbmSingleBlock;
|
|
InBlockSize: 0;
|
|
OutBlockSize: 128;
|
|
KeyBitsMin: 2048;
|
|
KeyBitsMax: 2048;
|
|
ContextSize: RSAOAEP1024ContextSize;
|
|
InitFunc: CipherRSAOAEP1024Init;
|
|
EncryptFunc: CipherRSAOAEP1024EncryptBuffer;
|
|
DecryptFunc: CipherRSAOAEP1024DecryptBuffer;
|
|
FinaliseFunc: CipherRSAOAEP1024Finalise;
|
|
)
|
|
{$ELSE}
|
|
(
|
|
ValidCipher: False;
|
|
ShortName: 'RSA-OAEP-1024';
|
|
BufferMode: cbmSingleBlock;
|
|
InBlockSize: 0;
|
|
OutBlockSize: 128;
|
|
KeyBitsMin: 1024;
|
|
KeyBitsMax: 1024;
|
|
ContextSize: 0;
|
|
InitFunc: nil;
|
|
EncryptFunc: nil;
|
|
DecryptFunc: nil;
|
|
FinaliseFunc: nil;
|
|
)
|
|
{$ENDIF}
|
|
);
|
|
|
|
function GetCipherInfo(const Cipher: TCipherType): PCipherInfo;
|
|
begin
|
|
Result := @CipherInfo[Cipher];
|
|
if not Result^.ValidCipher then
|
|
Result := nil;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Cipher }
|
|
{ }
|
|
function InitCipherState(
|
|
var State: TCipherState;
|
|
const Operation: TCipherOperation;
|
|
const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding;
|
|
const KeyBits: Integer): PCipherInfo;
|
|
var C : PCipherInfo;
|
|
begin
|
|
C := GetCipherInfo(Cipher);
|
|
if not Assigned(C) then
|
|
raise ECipher.Create(CipherError_InvalidCipher, 'Invalid cipher');
|
|
FillChar(State, SizeOf(State), 0);
|
|
State.Operation := Operation;
|
|
State.Cipher := Cipher;
|
|
State.Mode := Mode;
|
|
State.Padding := Padding;
|
|
State.KeyBits := KeyBits;
|
|
State.CipherInfo := C;
|
|
Result := C;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Encrypt }
|
|
{ }
|
|
procedure EncryptInit(
|
|
var State: TCipherState;
|
|
const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding;
|
|
const KeyBits: Integer;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer);
|
|
var C : PCipherInfo;
|
|
begin
|
|
C := InitCipherState(State, coEncrypt, Cipher, Mode, Padding, KeyBits);
|
|
if Assigned(C^.InitFunc) then
|
|
C^.InitFunc(coEncrypt, KeyBuffer, KeyBufferSize, KeyBits, @State.Context);
|
|
end;
|
|
|
|
procedure EncryptPadBuffer(
|
|
const Padding: TCipherPadding;
|
|
const Buffer: Pointer;
|
|
const InputBufferSize, OutputBufferSize: Integer);
|
|
var P : PByte;
|
|
I : Integer;
|
|
begin
|
|
P := Buffer;
|
|
Inc(P, InputBufferSize);
|
|
I := OutputBufferSize - InputBufferSize;
|
|
case Padding of
|
|
cpNone : FillChar(P^, I, 0);
|
|
cpPadSizeByte :
|
|
begin
|
|
FillChar(P^, I - 1, 0);
|
|
Assert(I <= $FF);
|
|
Inc(P, I - 1);
|
|
P^ := Byte(I);
|
|
end;
|
|
cpPadPKCS5 :
|
|
begin
|
|
Assert(I <= $FF);
|
|
FillChar(P^, I, Char(I));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function EncryptedSize(
|
|
const Cipher: TCipherType; const Padding: TCipherPadding;
|
|
const BufferSize: Integer): Integer;
|
|
var C : PCipherInfo;
|
|
B : Integer;
|
|
begin
|
|
if BufferSize < 0 then
|
|
begin
|
|
Result := 0;
|
|
exit;
|
|
end;
|
|
C := GetCipherInfo(Cipher);
|
|
case C^.BufferMode of
|
|
cbmMultipleBlocks :
|
|
begin
|
|
Result := BufferSize;
|
|
B := C^.OutBlockSize;
|
|
Assert((Padding = cpNone) or (B <= $FF));
|
|
case Padding of
|
|
cpNone : ;
|
|
cpPadSizeByte : Inc(Result);
|
|
cpPadPKCS5 :
|
|
if Result mod B = 0 then
|
|
Inc(Result, B);
|
|
end;
|
|
Result := ((Result + (B - 1)) div B) * B;
|
|
end;
|
|
cbmSingleBlock : Result := C^.OutBlockSize;
|
|
cbmStream : Result := BufferSize;
|
|
else
|
|
Result := BufferSize;
|
|
end;
|
|
end;
|
|
|
|
function EncryptBuf(
|
|
var State: TCipherState;
|
|
const InputBuffer: Pointer; const InputBufferSize: Integer;
|
|
const OutputBuffer: Pointer; const OutputBufferSize: Integer;
|
|
const InitVectorBuffer: Pointer; const InitVectorBufferSize: Integer): Integer;
|
|
var C : PCipherInfo;
|
|
P : Pointer;
|
|
begin
|
|
Assert(State.Operation = coEncrypt);
|
|
Assert(Assigned(State.CipherInfo));
|
|
|
|
C := State.CipherInfo;
|
|
// validate parameters
|
|
Result := EncryptedSize(State.Cipher, State.Padding, InputBufferSize);
|
|
if Result = 0 then
|
|
exit;
|
|
if Result > OutputBufferSize then
|
|
raise ECipher.Create(CipherError_InvalidBufferSize, 'Output buffer too small');
|
|
if not Assigned(InputBuffer) or not Assigned(OutputBuffer) then
|
|
raise ECipher.Create(CipherError_InvalidBuffer, 'Invalid buffer');
|
|
// encrypt
|
|
if InputBuffer <> OutputBuffer then
|
|
Move(InputBuffer^, OutputBuffer^, InputBufferSize);
|
|
// pad buffer
|
|
if C^.BufferMode = cbmMultipleBlocks then
|
|
if InputBufferSize < Result then
|
|
EncryptPadBuffer(State.Padding, OutputBuffer, InputBufferSize, Result);
|
|
// encrypt buffer
|
|
P := @State.Context;
|
|
if C^.BufferMode = cbmSingleBlock then
|
|
CipherBuffer(OutputBuffer, Result, P, C^.EncryptFunc, C^.InBlockSize)
|
|
else
|
|
case State.Mode of
|
|
cmECB : ECBBuffer(OutputBuffer, Result, P, C^.EncryptFunc, C^.InBlockSize);
|
|
cmCBC : CBCEncode(OutputBuffer, Result, P, C^.EncryptFunc, C^.InBlockSize, InitVectorBuffer, InitVectorBufferSize);
|
|
cmCFB : CFBEncode(OutputBuffer, Result, P, C^.EncryptFunc, C^.InBlockSize, InitVectorBuffer, InitVectorBufferSize);
|
|
cmOFB : OFBEncode(OutputBuffer, Result, P, C^.EncryptFunc, C^.InBlockSize, InitVectorBuffer, InitVectorBufferSize);
|
|
else
|
|
raise ECipher.Create(CipherError_InvalidCipherMode, 'Invalid cipher mode');
|
|
end;
|
|
end;
|
|
|
|
procedure EncryptFinalise(var State: TCipherState);
|
|
var C : PCipherInfo;
|
|
begin
|
|
C := State.CipherInfo;
|
|
if Assigned(C) then
|
|
if Assigned(C^.FinaliseFunc) then
|
|
C^.FinaliseFunc(@State.Context);
|
|
SecureClear(State, SizeOf(State));
|
|
end;
|
|
|
|
function Encrypt(const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding;
|
|
const KeyBits: Integer;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const InputBuffer: Pointer; const InputBufferSize: Integer;
|
|
const OutputBuffer: Pointer; const OutputBufferSize: Integer;
|
|
const InitVectorBuffer: Pointer; const InitVectorBufferSize: Integer): Integer;
|
|
var State : TCipherState;
|
|
begin
|
|
EncryptInit(
|
|
State, Cipher, Mode, Padding,
|
|
KeyBits, KeyBuffer, KeyBufferSize);
|
|
Result := EncryptBuf(
|
|
State,
|
|
InputBuffer, InputBufferSize, OutputBuffer, OutputBufferSize,
|
|
InitVectorBuffer, InitVectorBufferSize);
|
|
EncryptFinalise(State);
|
|
end;
|
|
|
|
function Encrypt(const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding; const KeyBits: Integer;
|
|
const Key, Data, InitVector: RawByteString): RawByteString;
|
|
var L, M : Integer;
|
|
begin
|
|
M := Length(Data);
|
|
L := EncryptedSize(Cipher, Padding, M);
|
|
SetLength(Result, L);
|
|
if L = 0 then
|
|
exit;
|
|
Encrypt(Cipher, Mode, Padding, KeyBits, PByteChar(Key), Length(Key),
|
|
PByteChar(Data), M, PByteChar(Result), L,
|
|
PByteChar(InitVector), Length(InitVector));
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Decrypt }
|
|
{ }
|
|
procedure DecryptInit(
|
|
var State: TCipherState;
|
|
const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding;
|
|
const KeyBits: Integer;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer);
|
|
var C : PCipherInfo;
|
|
begin
|
|
C := InitCipherState(State, coDecrypt, Cipher, Mode, Padding, KeyBits);
|
|
if Assigned(C^.InitFunc) then
|
|
if Mode in [cmCFB, cmOFB] then
|
|
C^.InitFunc(coEncrypt, KeyBuffer, KeyBufferSize, KeyBits, @State.Context)
|
|
else
|
|
C^.InitFunc(coDecrypt, KeyBuffer, KeyBufferSize, KeyBits, @State.Context)
|
|
end;
|
|
|
|
function DecryptBuf(
|
|
var State: TCipherState;
|
|
const Buffer: Pointer; const BufferSize: Integer;
|
|
const InitVectorBuffer: Pointer; const InitVectorBufferSize: Integer): Integer;
|
|
var C : PCipherInfo;
|
|
P : PByte;
|
|
D : Pointer;
|
|
I : Integer;
|
|
L : Byte;
|
|
N : Integer;
|
|
begin
|
|
Assert(State.Operation = coDecrypt);
|
|
Assert(Assigned(State.CipherInfo));
|
|
|
|
C := State.CipherInfo;
|
|
// decrypt buffer
|
|
D := @State.Context;
|
|
if C^.BufferMode = cbmSingleBlock then
|
|
CipherBuffer(Buffer, BufferSize, D, C^.DecryptFunc, C^.InBlockSize)
|
|
else
|
|
case State.Mode of
|
|
cmECB : ECBBuffer(Buffer, BufferSize, D, C^.DecryptFunc, C^.InBlockSize);
|
|
cmCBC : CBCDecode(Buffer, BufferSize, D, C^.DecryptFunc, C^.InBlockSize, InitVectorBuffer, InitVectorBufferSize);
|
|
cmCFB : CFBDecode(Buffer, BufferSize, D, C^.EncryptFunc, C^.InBlockSize, InitVectorBuffer, InitVectorBufferSize);
|
|
cmOFB : OFBDecode(Buffer, BufferSize, D, C^.EncryptFunc, C^.InBlockSize, InitVectorBuffer, InitVectorBufferSize);
|
|
else
|
|
raise ECipher.Create(CipherError_InvalidCipherMode, 'Invalid cipher mode');
|
|
end;
|
|
N := BufferSize;
|
|
// remove padding
|
|
if C^.BufferMode = cbmMultipleBlocks then
|
|
if State.Padding in [cpPadSizeByte, cpPadPKCS5] then
|
|
begin
|
|
// get pad size byte (last byte)
|
|
P := Buffer;
|
|
Inc(P, BufferSize - 1);
|
|
L := P^;
|
|
// validate padding
|
|
if (L > N) or (L > C^.InBlockSize) then
|
|
raise ECipher.Create(CipherError_InvalidData, 'Invalid data');
|
|
for I := 1 to L - 1 do
|
|
begin
|
|
Dec(P);
|
|
if ((State.Padding = cpPadSizeByte) and (P^ <> 0)) or
|
|
((State.Padding = cpPadPKCS5) and (P^ <> L)) then
|
|
raise ECipher.Create(CipherError_InvalidData, 'Invalid data');
|
|
end;
|
|
// decrease buffer size with padding size
|
|
Dec(N, L);
|
|
end;
|
|
// return number of decoded bytes
|
|
Result := N;
|
|
end;
|
|
|
|
procedure DecryptFinalise(var State: TCipherState);
|
|
var C : PCipherInfo;
|
|
begin
|
|
C := State.CipherInfo;
|
|
if Assigned(C) then
|
|
if Assigned(C^.FinaliseFunc) then
|
|
C^.FinaliseFunc(@State.Context);
|
|
SecureClear(State, SizeOf(State));
|
|
end;
|
|
|
|
function Decrypt(const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding;
|
|
const KeyBits: Integer;
|
|
const KeyBuffer: Pointer; const KeyBufferSize: Integer;
|
|
const Buffer: Pointer; const BufferSize: Integer;
|
|
const InitVectorBuffer: Pointer; const InitVectorBufferSize: Integer): Integer; overload;
|
|
var State : TCipherState;
|
|
begin
|
|
DecryptInit(State, Cipher, Mode, Padding, KeyBits, KeyBuffer, KeyBufferSize);
|
|
Result := DecryptBuf(State, Buffer, BufferSize, InitVectorBuffer, InitVectorBufferSize);
|
|
DecryptFinalise(State);
|
|
end;
|
|
|
|
function Decrypt(const Cipher: TCipherType; const Mode: TCipherMode;
|
|
const Padding: TCipherPadding; const KeyBits: Integer;
|
|
const Key, Data, InitVector: RawByteString): RawByteString;
|
|
var L, M : Integer;
|
|
begin
|
|
L := Length(Data);
|
|
SetLength(Result, L);
|
|
if L = 0 then
|
|
exit;
|
|
Move(Pointer(Data)^, Pointer(Result)^, L);
|
|
M := Decrypt(Cipher, Mode, Padding, KeyBits, PByteChar(Key), Length(Key),
|
|
PByteChar(Result), L,
|
|
PByteChar(InitVector), Length(InitVector));
|
|
if M < L then
|
|
SetLength(Result, M);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Test }
|
|
{ }
|
|
{$IFDEF CIPHER_TEST}
|
|
{$ASSERTIONS ON}
|
|
procedure Test;
|
|
begin
|
|
Assert(RC2ContextSize = 128);
|
|
Assert(RC2ContextSize <= MaxCipherContextSize);
|
|
Assert(RC4ContextSize <= MaxCipherContextSize);
|
|
Assert(DESContextSize <= MaxCipherContextSize);
|
|
Assert(TripleDESContextSize <= MaxCipherContextSize);
|
|
Assert(TripleDES3ContextSize <= MaxCipherContextSize);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
end.
|
|
|