FastReport_FMX_2.8.12/LibD28/FMX.frxCrypto.pas

1457 lines
29 KiB
ObjectPascal
Raw Permalink Normal View History

2024-01-10 21:50:38 +01:00
{******************************************}
{ }
{ FastReport FMX v2.0 }
{ Cryptography }
{ }
{ Copyright (c) 1998-2013 }
{ by Anton Khayrudinov }
{ Fast Reports Inc. }
{ }
{******************************************}
unit FMX.frxCrypto;
{ Disable overflow checking.
Cryptography operates in finite fields
and integer oveflow is meaningless for it. }
{$Q-}
interface
uses
System.SysUtils,
System.Classes;
type
{ RC4 cipher }
TCryptoRC4 = class
private
FS: array[0..255] of Byte;
FI, FJ: Byte;
public
{ Initializes the RC4 cipher with a specified key }
procedure Init(const Key; Len: LongInt);
{ Encrypts a data block "in place".
The RC4 cipher generates a crypto stream (based on the password),
that is combined with the original data with XOR operation.
If Data is nil, the crypto stream is generated but is written
nowhere. }
procedure Encrypt(Data: Pointer; Size: LongInt);
end;
{ AES cipher. FIPS 197. }
TCryptoAESMatrix = array[0..15] of Byte; // matrix 4x4
TCryptoAESSBox = array[0..255] of Byte;
TCryptoAESDiffuseRow = array[0..3] of Byte;
TCryptoAES = class
private
FNr: Integer;
FW: array of TCryptoAESMatrix;
class procedure InitSBox;
class function Mul8(a, b: Integer): Integer;
protected
class procedure MatrixToData(out Data; const a: TCryptoAESMatrix);
class procedure DataToMatrix(out a: TCryptoAESMatrix; const Data);
procedure Sum(var a: TCryptoAESMatrix; const b, c: TCryptoAESMatrix);
procedure Mov(var a: TCryptoAESMatrix; const b: TCryptoAESMatrix);
procedure Zero(var a: TCryptoAESMatrix);
procedure Apply(var a: TCryptoAESMatrix; const Box: TCryptoAESSBox);
procedure Rotate(var a: TCryptoAESMatrix; Dir: Integer);
procedure Diffuse(var a: TCryptoAESMatrix; const DiffuseRow: TCryptoAESDiffuseRow);
procedure ExpandKey(const Key: array of Byte);
procedure Encrypt(var a: TCryptoAESMatrix); overload; virtual;
procedure Decrypt(var a: TCryptoAESMatrix); overload; virtual;
public
{ Key length can be 16, 24 or 32 bytes }
constructor Create(const Key: array of Byte);
{ Encrypts/decrypts a 16-byte block }
procedure Encrypt(var Output; const Input); overload; virtual;
procedure Decrypt(var Output; const Input); overload; virtual;
end;
ECryptoAESException = class(Exception);
ECryptoAESInvalidKeyLength = class(ECryptoAESException);
ECryptoAESInvalidBlockSize = class(ECryptoAESException);
{ AES CBC mode }
TCryptoAES_CBC = class(TCryptoAES)
private
FC: TCryptoAESMatrix;
protected
procedure Encrypt(var a: TCryptoAESMatrix); override;
procedure Decrypt(var a: TCryptoAESMatrix); override;
public
{ IV has 16 bytes }
constructor Create(const Key: array of Byte; const IV);
end;
{ lag-r CMWC random number generator with base 2**32 }
TCryptoCMWC = class
private
FMultiplier: LongWord;
FSeed: array of LongWord;
FCarry: LongWord;
FNext: LongInt;
public
constructor Create(a: Cardinal = 3636507990; r: Cardinal = 1359);
function Next: LongWord;
end;
{ Hash-function interface.
Typical use of a hash function is following:
h := TCryptoHash.Create('SHA1');
h.Push(@Message, Length(Message));
h.GetDigest(@Digest);
h.Free;
If several digests are computed with one TCryptoHash
instance (in order to save resources), then the following
way can be used:
h := TCryptoHash.Create('SHA1');
h.Reset; // optional
h.Push(@Message1, Length(Message1));
h.GetDigest(@Digest1);
h.Reset; // mandatory
h.Push(@Message2, Length(Message2));
h.GetDigest(@Digest2);
h.Reset; // mandatory
h.Push(@Message2, Length(Message2));
h.GetDigest(@Digest2);
h.Free;
Message can be sent by parts:
h := TCryptoHash.Create('SHA1');
h.Push(@MessagePart1, Length(MessagePart1));
h.Push(@MessagePart2, Length(MessagePart2));
h.Push(@MessagePart3, Length(MessagePart3));
h.GetDigest(@Digest);
h.Free; }
TCryptoHash = class
private
FChunk: array of Byte;
FLength: Integer;
protected
constructor Create(ChunkSize: Integer); overload;
procedure Process(const Chunk: array of Byte); virtual; abstract;
procedure Finish(LengthSize: Integer; Straight: Boolean); virtual;
public
{ Creates an instance of a hash function.
Acceptable names are:
MD5
SHA1
Whirlpool
Jenkins
If the name is not recognised, nil is returned. }
class function Create(const Name: string): TCryptoHash; overload;
{ Calculates a hash of a text. Note that different hash functions
produce digests of different lengths. }
class procedure Hash(const Name: string; var Digest; DigestSize: Integer;
const Data; Size: Integer); overload;
class procedure Hash(const Name: string; var Digest; DigestSize: Integer;
const s: AnsiString); overload;
{ Resets the state of the hash function.
This method must be called if more than one digest
is computed with one TCryptoHash instance. }
procedure Reset; virtual;
{ Sends a piece of message to the hash function }
procedure Push(b: Byte); overload;
procedure Push(const Data; Size: Integer); overload;
procedure Push(Stream: TStream); overload;
{ Returns message digest. This function should not be called several times in a row. }
procedure GetDigest(out Digest; Size: Integer); virtual; abstract;
{ Returns size of the digest in bytes }
function DigestSize: Integer; virtual; abstract;
{ Block hashes compute read a message by chunks of fixed size.
It's most efficient to send a message to a hash function by chunks
of this size. }
function ChunkSize: Integer;
end;
ECryptoHash = class(Exception);
ECryptoHashUnknown = class(ECryptoHash)
public
constructor Create(Name: string);
end;
{ MD5 hash. RFC 1321. Digest length: 16 bytes. }
TCryptoMD5 = class(TCryptoHash)
private
FState: array[0..3] of Integer;
class procedure InitSinTable;
procedure InitState;
protected
procedure Process(const Chunk: array of Byte); override;
public
constructor Create;
procedure GetDigest(out Digest; Size: Integer); override;
procedure Reset; override;
function DigestSize: Integer; override;
end;
{ SHA1 hash. RFC 3174. Digest length: 20 bytes. }
TCryptoSHA1 = class(TCryptoHash)
private
FState: array[0..4] of Integer;
procedure InitState;
protected
procedure Process(const Chunk: array of Byte); override;
public
constructor Create;
procedure GetDigest(out Digest; Size: Integer); override;
procedure Reset; override;
function DigestSize: Integer; override;
end;
{ Whirlpool hash. ISO/IEC 10118-3. Digest length: 64 bytes. }
TCryptoWhirlpoolMatrix = array[0..7, 0..7] of Byte;
TCryptoWhirlpool = class(TCryptoHash)
private
FState: TCryptoWhirlpoolMatrix;
procedure ApplySBox(var a: TCryptoWhirlpoolMatrix);
procedure Rotate(var a: TCryptoWhirlpoolMatrix);
procedure Diffuse(var a: TCryptoWhirlpoolMatrix);
procedure Transform(var a: TCryptoWhirlpoolMatrix; const b: TCryptoWhirlpoolMatrix);
procedure InitState;
procedure Sum(var a: TCryptoWhirlpoolMatrix; const b, c: TCryptoWhirlpoolMatrix);
procedure Encrypt(var w: TCryptoWhirlpoolMatrix; const a, b: TCryptoWhirlpoolMatrix);
protected
procedure Process(const Chunk: array of Byte); override;
private
class procedure InitSBox;
class procedure InitMul8;
class function Mul4(a, b: Integer): Integer;
class function Mul8(a, b: Integer): Integer;
class function Mul8NoCache(a, b: Integer): Integer;
public
constructor Create;
procedure GetDigest(out Digest; Size: Integer); override;
procedure Reset; override;
function DigestSize: Integer; override;
end;
{ This is a modification of the Jenkins hash.
It's noncryptographic, but fast. }
TCryptoJenkins = class(TCryptoHash)
private
FState: Cardinal;
protected
procedure Finish(LengthSize: Integer; Straight: Boolean); override;
procedure Process(const Chunk: array of Byte); override;
public
constructor Create;
procedure GetDigest(out Digest; Size: Integer); override;
procedure Reset; override;
function DigestSize: Integer; override;
end;
implementation
uses
System.Math;
{ Delphi4 doesn't allow to put these variables as
static class fields of corresponding classes. }
var
AESSBox, AESIBox: TCryptoAESSBox;
MD5SinTable: array[1..64] of Integer;
WhirlpoolSBox: array[0..255] of Byte;
WhirlpoolMul8: array[1..9, 0..255] of Byte;
{ Returns N bytes from Base + Offset }
function GetNBytesAt(const Base; Offset, N: Integer): Integer;
begin
Assert(SizeOf(Result) >= N);
Result := 0;
Move(Pointer(NativeInt(@Base) + Offset)^, Result, N);
end;
procedure SetNBytesAt(var Base; Offset, N, Value: Integer);
begin
Assert(SizeOf(Value) >= N);
Move(Value, Pointer(NativeInt(@Base) + Offset)^, N);
end;
function Min(a, b: Integer): Integer;
begin
if a < b then
Result := a
else
Result := b
end;
{ Rotates a 32-bit integer by n bits left }
function RotLeft(a, n: Integer): Integer;
var
p, q: Integer;
begin
p := Integer(Cardinal(a) shr (32 - n));
q := a and ((Cardinal(1) shl (32 - n)) - 1);
Result := (q shl n) xor p;
end;
{ Swap bytes of a 32-bit integer }
function ByteSwap(x: Integer): Integer;
var
b: array[0..3] of Byte;
i: Integer;
begin
for i := 0 to 3 do
begin
b[i] := x and $ff;
x := x shr 8;
end;
Result := 0;
for i := 0 to 3 do
Result := (Result shl 8) xor b[i];
end;
{ TCryptoCMWC }
constructor TCryptoCMWC.Create(a, r: Cardinal);
var
t: Extended;
i: LongInt;
h: array[0..15] of Byte;
begin
FMultiplier := a;
SetLength(FSeed, r);
{ Initialise the first r seed values with some data }
t := Time;
TCryptoHash.Hash('MD5', h, Length(h), t, SizeOf(t));
for i := 0 to Length(FSeed) - 1 do
begin
FSeed[i] := 0;
Move(h[(i mod 4)*4], FSeed[i], 4);
end;
end;
function TCryptoCMWC.Next: LongWord;
const
Mask: Cardinal = $ffffffff;
var
q: UInt64;
begin
q := UInt64(FMultiplier)*FSeed[FNext] + FCarry;
FSeed[FNext] := q and Mask;
FCarry := (q shr 32) and Mask;
Result := FSeed[FNext];
Inc(FNext);
if FNext = Length(FSeed) then
FNext := 0;
end;
{ TCryptoRC4 }
procedure TCryptoRC4.Init(const Key; Len: LongInt);
var
i, j, k: Byte;
b: Byte;
begin
for i := 0 to 255 do
FS[i] := i;
j := 0;
k := 0;
for i := 0 to 255 do
begin
j := (Integer(j) + GetNBytesAt(Key, k, 1) + FS[i]) and $ff;
k := k + 1;
if k = len then
k := 0;
b := FS[i];
FS[i] := FS[j];
FS[j] := b;
end;
FI := 0;
FJ := 0;
end;
procedure TCryptoRC4.Encrypt(Data: Pointer; Size: LongInt);
var
i: Integer;
s, h: Byte;
begin
for i := 0 to Size - 1 do
begin
Inc(FI);
Inc(FJ, FS[FI]);
s := FS[FJ];
FS[FJ] := FS[FI];
FS[FI] := s;
if Data <> nil then
begin
h := FS[(FS[FI] + FS[FJ]) and $ff];
SetNBytesAt(Data^, i, 1, GetNBytesAt(Data^, i, 1) xor h);
end;
end;
end;
{ TCryptoHash }
function TCryptoHash.ChunkSize: Integer;
begin
Result := Length(FChunk);
end;
class function TCryptoHash.Create(const Name: string): TCryptoHash;
begin
if Name = 'MD5' then
Result := TCryptoMD5.Create
else if Name = 'SHA1' then
Result := TCryptoSHA1.Create
else if Name = 'Whirlpool' then
Result := TCryptoWhirlpool.Create
else if Name = 'Jenkins' then
Result := TCryptoJenkins.Create
else
Result := nil
end;
class procedure TCryptoHash.Hash(const Name: string; var Digest; DigestSize: Integer;
const Data; Size: Integer);
var
h: TCryptoHash;
begin
h := Create(Name);
if h = nil then
raise ECryptoHashUnknown.Create(Name);
try
h.Push(Data, Size);
h.GetDigest(Digest, DigestSize);
finally
h.Free;
end;
end;
constructor TCryptoHash.Create(ChunkSize: Integer);
begin
SetLength(FChunk, ChunkSize);
end;
class procedure TCryptoHash.Hash(const Name: string; var Digest; DigestSize: Integer;
const s: AnsiString);
begin
if s = '' then
Hash(Name, Digest, DigestSize, nil^, 0)
else
Hash(Name, Digest, DigestSize, s[1], Length(s))
end;
procedure TCryptoHash.Push(b: Byte);
begin
FChunk[FLength mod Length(FChunk)] := b;
Inc(FLength);
if FLength mod Length(FChunk) = 0 then
Process(FChunk);
end;
procedure TCryptoHash.Push(const Data; Size: Integer);
var
Used, n: Integer;
Offset: Integer;
begin
{ The following code is equal to:
for n := 0 to Size - 1 do
Push(PByte(Integer(Data) + n)^) }
Used := FLength mod ChunkSize;
Inc(FLength, Size);
Offset := 0;
while Size > 0 do
begin
n := Min(Size, ChunkSize - Used);
Move(Pointer(NativeInt(@Data) + Offset)^, FChunk[Used], n);
Inc(Offset, n);
Inc(Used, n);
Dec(Size, n);
if Used = ChunkSize then
begin
Process(FChunk);
Used := 0;
end;
end;
end;
procedure TCryptoHash.Reset;
begin
FLength := 0;
end;
procedure TCryptoHash.Finish(LengthSize: Integer; Straight: Boolean);
var
i, n, cn: Integer;
begin
cn := Length(FChunk);
n := FLength*8;
Push($80);
while FLength mod cn <> cn - LengthSize do
Push(0);
if Straight then
for i := cn - LengthSize to cn - 1 do
begin
FChunk[i] := n and $ff;
n := n shr 8;
end
else
for i := cn - 1 downto cn - LengthSize do
begin
FChunk[i] := n and $ff;
n := n shr 8;
end;
Process(FChunk);
end;
procedure TCryptoHash.Push(Stream: TStream);
var
Buffer: array of Byte;
n: Integer;
begin
Stream.Position := 0;
SetLength(Buffer, ChunkSize);
n := Stream.Read(Buffer[0], Length(Buffer));
while n > 0 do
begin
Push(Buffer[0], n);
n := Stream.Read(Buffer[0], Length(Buffer));
end;
end;
{ TCryptoMD5 }
constructor TCryptoMD5.Create;
begin
inherited Create(64);
InitState;
end;
procedure TCryptoMD5.InitState;
begin
FState[0] := ByteSwap(Integer($01234567));
FState[1] := ByteSwap(Integer($89abcdef));
FState[2] := ByteSwap(Integer($fedcba98));
FState[3] := ByteSwap(Integer($76543210));
end;
class procedure TCryptoMD5.InitSinTable;
function AbsSin(x: Extended): Extended;
begin
Result := Sin(x);
if Result < 0 then
Result := -Result;
end;
var
i: Integer;
begin
for i := 1 to 64 do
MD5SinTable[i] := Floor($100000000*AbsSin(i))
end;
procedure TCryptoMD5.Process(const Chunk: array of Byte);
function X(i: Integer): Integer;
begin
Result := GetNBytesAt(Chunk[4*i], 0, 4)
end;
function F(i, x, y, z: Integer): Integer;
begin
case i of
00..15: Result := x and y or not x and z;
16..31: Result := x and z or y and not z;
32..47: Result := x xor y xor z;
48..63: Result := y xor (x or not z);
else Result := 0;
end;
end;
const
G: array[0..63, 0..1] of Integer =
(
{ Round 1 }
( 0, 7),
( 1, 12),
( 2, 17),
( 3, 22),
( 4, 7),
( 5, 12),
( 6, 17),
( 7, 22),
( 8, 7),
( 9, 12),
(10, 17),
(11, 22),
(12, 7),
(13, 12),
(14, 17),
(15, 22),
{ Round 2 }
( 1, 5),
( 6, 9),
(11, 14),
( 0, 20),
( 5, 5),
(10, 9),
(15, 14),
( 4, 20),
( 9, 5),
(14, 9),
( 3, 14),
( 8, 20),
(13, 5),
( 2, 9),
( 7, 14),
(12, 20),
{ Round 3 }
( 5, 4),
( 8, 11),
(11, 16),
(14, 23),
( 1, 4),
( 4, 11),
( 7, 16),
(10, 23),
(13, 4),
( 0, 11),
( 3, 16),
( 6, 23),
( 9, 4),
(12, 11),
(15, 16),
( 2, 23),
{ Round 4 }
( 0, 6),
( 7, 10),
(14, 15),
( 5, 21),
(12, 6),
( 3, 10),
(10, 15),
( 1, 21),
( 8, 6),
(15, 10),
( 6, 15),
(13, 21),
( 4, 6),
(11, 10),
( 2, 15),
( 9, 21)
);
var
i, j, k: Integer;
W, R: array[0..3] of Integer;
begin
for j := 0 to 3 do
W[j] := FState[j];
for i := 0 to 63 do
begin
for j := 0 to 3 do
R[j] := W[(j - i mod 16) and 3];
k := R[0] + X(G[i, 0]) + MD5SinTable[i + 1] + F(i, R[1], R[2], R[3]);
W[(0 - i mod 16) and 3] := R[1] + RotLeft(k, G[i, 1]);
end;
for j := 0 to 3 do
Inc(FState[j], W[j]);
end;
procedure TCryptoMD5.Reset;
begin
inherited;
InitState;
end;
function TCryptoMD5.DigestSize: Integer;
begin
Result := 16;
end;
procedure TCryptoMD5.GetDigest(out Digest; Size: Integer);
begin
Assert(Size = DigestSize);
Finish(8, True);
Move(FState, Digest, Size);
end;
{ TCryptoSHA1 }
constructor TCryptoSHA1.Create;
begin
inherited Create(64);
InitState;
end;
function TCryptoSHA1.DigestSize: Integer;
begin
Result := 20;
end;
procedure TCryptoSHA1.GetDigest(out Digest; Size: Integer);
var
i: Integer;
begin
Assert(Size = DigestSize);
Finish(8, False);
for i := 0 to 4 do
SetNBytesAt(Digest, i*4, 4, ByteSwap(FState[i]));
end;
procedure TCryptoSHA1.InitState;
begin
FState[0] := ByteSwap(Integer($01234567));
FState[1] := ByteSwap(Integer($89abcdef));
FState[2] := ByteSwap(Integer($fedcba98));
FState[3] := ByteSwap(Integer($76543210));
FState[4] := ByteSwap(Integer($f0e1d2c3));
end;
procedure TCryptoSHA1.Process(const Chunk: array of Byte);
function K(i: Integer): Integer;
begin
case i of
00..19: Result := Integer($5a827999);
20..39: Result := Integer($6ed9eba1);
40..59: Result := Integer($8f1bbcdc);
60..79: Result := Integer($ca62c1d6);
else Result := 0
end
end;
function F(t, b, c, d: Integer): Integer;
begin
case t of
00..19: Result := b and c or not b and d;
20..39: Result := b xor c xor d;
40..59: Result := b and c or b and d or c and d;
60..79: Result := b xor c xor d;
else Result := 0
end
end;
var
H: array[0..4] of Integer;
procedure Shuffle(t: Integer);
begin
H[4] := H[3];
H[3] := H[2];
H[2] := H[1];
H[1] := H[0];
H[0] := t;
H[2] := RotLeft(H[2], 30);
end;
var
i, j: Integer;
W: array[0..79] of Integer;
begin
for i := 0 to 15 do
W[i] := ByteSwap(GetNBytesAt(Chunk[i*4], 0, 4));
for i := 16 to 79 do
W[i] := RotLeft(W[i - 3] xor W[i - 8] xor W[i - 14] xor W[i - 16], 1);
for j := 0 to 4 do
H[j] := FState[j];
for i := 0 to 79 do
Shuffle(RotLeft(H[0], 5) + F(i, H[1], H[2], H[3]) + H[4] + W[i] + K(i));
for j := 0 to 4 do
Inc(FState[j], H[j]);
end;
procedure TCryptoSHA1.Reset;
begin
inherited;
InitState;
end;
{ TCryptoWhirlpool }
class procedure TCryptoWhirlpool.InitSBox;
const
R: array[0..15] of Byte = (7, 12, 11, 13, 14, 4, 9, 15, 6, 3, 8, 10, 2, 5, 1, 0);
var
s, u, v, a: Integer;
E, I: array[0..15] of Byte;
begin
E[0] := 1;
E[15] := 0;
for s := 1 to 14 do
E[s] := Mul4($b, E[s - 1]);
for s := 0 to 15 do
I[E[s]] := s;
for s := 0 to 255 do
begin
u := s shr 4;
v := s and $f;
a := R[E[u] xor I[v]];
u := E[E[u] xor a];
v := I[I[v] xor a];
WhirlpoolSBox[s] := (u shl 4) xor v;
end;
end;
class function TCryptoWhirlpool.Mul4(a, b: Integer): Integer;
begin
Result := 0;
while b <> 0 do
begin
if b and 1 = 1 then
Result := Result xor a;
b := b shr 1;
a := a shl 1;
if a and $10 <> 0 then
a := a xor $13;
end;
end;
class function TCryptoWhirlpool.Mul8(a, b: Integer): Integer;
begin
{ If an exception happens here, then Whirlpool is incorrectly initialised.
Before using Whirlpool, its Mul8 cache must be filled in for all values
in the diffuse row. }
Result := WhirlpoolMul8[a, b]
end;
class procedure TCryptoWhirlpool.InitMul8;
var
a, b: Integer;
begin
for a := 1 to 9 do
for b := 0 to 255 do
WhirlpoolMul8[a, b] := Mul8NoCache(a, b);
end;
class function TCryptoWhirlpool.Mul8NoCache(a, b: Integer): Integer;
begin
Result := 0;
while b <> 0 do
begin
if b and 1 = 1 then
Result := Result xor a;
b := b shr 1;
a := a shl 1;
if a and $100 <> 0 then
a := a xor $11d;
end;
end;
procedure TCryptoWhirlpool.ApplySBox(var a: TCryptoWhirlpoolMatrix);
var
i, j: Integer;
begin
for i := 0 to 7 do
for j := 0 to 7 do
a[i, j] := WhirlpoolSBox[a[i, j]]
end;
procedure TCryptoWhirlpool.Rotate(var a: TCryptoWhirlpoolMatrix);
var
i, j: Integer;
Col: array[0..7] of Byte;
begin
for j := 0 to 7 do
begin
for i := 0 to 7 do
Col[i] := a[(i - j) and 7, j];
for i := 0 to 7 do
a[i, j] := Col[i];
end;
end;
procedure TCryptoWhirlpool.Sum(var a: TCryptoWhirlpoolMatrix; const b, c: TCryptoWhirlpoolMatrix);
var
i, j: Integer;
begin
for i := 0 to 7 do
for j := 0 to 7 do
a[i, j] := b[i, j] xor c[i, j];
end;
procedure TCryptoWhirlpool.Diffuse(var a: TCryptoWhirlpoolMatrix);
const
DiffuseRow: array[0..7] of Byte = (1, 1, 4, 1, 8, 5, 2, 9);
var
i, j, k: Integer;
Row: array[0..7] of Byte;
begin
for i := 0 to 7 do
begin
for j := 0 to 7 do
Row[j] := 0;
for j := 0 to 7 do
for k := 0 to 7 do
Row[j] := Row[j] xor Mul8(DiffuseRow[(j - k) and 7], a[i, k]);
for j := 0 to 7 do
a[i, j] := Row[j];
end;
end;
function TCryptoWhirlpool.DigestSize: Integer;
begin
Result := 64;
end;
procedure TCryptoWhirlpool.Encrypt(var w: TCryptoWhirlpoolMatrix; const a, b: TCryptoWhirlpoolMatrix);
var
c, k: TCryptoWhirlpoolMatrix;
i, r: Integer;
begin
Sum(w, a, b);
Sum(c, c, c);
Sum(k, a, c);
for r := 1 to 10 do
begin
for i := 0 to 7 do
c[0, i] := WhirlpoolSBox[8*(r - 1) + i];
Transform(k, c);
Transform(w, k);
end;
end;
procedure TCryptoWhirlpool.Transform(var a: TCryptoWhirlpoolMatrix; const b: TCryptoWhirlpoolMatrix);
begin
ApplySBox(a);
Rotate(a);
Diffuse(a);
Sum(a, a, b);
end;
procedure TCryptoWhirlpool.InitState;
begin
Sum(FState, FState, FState);
end;
procedure TCryptoWhirlpool.GetDigest(out Digest; Size: Integer);
var
i: Integer;
begin
Assert(Size = DigestSize);
Finish(32, False);
for i := 0 to 7 do
Move(FState[i, 0], Pointer(NativeInt(@Digest) + i*8)^, 8);
end;
procedure TCryptoWhirlpool.Reset;
begin
inherited;
InitState;
end;
procedure TCryptoWhirlpool.Process(const Chunk: array of Byte);
var
i, j: Integer;
b, w: TCryptoWhirlpoolMatrix;
begin
for i := 0 to 7 do
for j := 0 to 7 do
b[i, j] := Chunk[i*8 + j];
Encrypt(w, FState, b);
Sum(FState, FState, w);
Sum(FState, FState, b);
end;
constructor TCryptoWhirlpool.Create;
begin
inherited Create(64);
InitState;
end;
{ TCryptoAES }
class procedure TCryptoAES.InitSBox;
function XorBits(b: Byte): Byte;
var
i: Integer;
begin
Result := 0;
for i := 0 to 7 do
Result := Result xor ((b shr i) and 1);
end;
var
i, b: Integer;
m, r, q: Byte;
begin
for b := 0 to 255 do
begin
m := $f8;
r := 0;
{ Find q<>b = 1 }
if b = 0 then
q := 0
else
for q := 1 to 255 do
if Mul8(b, q) = 1 then
Break;
for i := 0 to 7 do
begin
r := (r shl 1) xor XorBits(q and m);
m := (m shr 1) xor Byte(m shl 7);
end;
AESSBox[b] := r xor $63;
AESIBox[AESSBox[b]] := b;
end;
end;
class function TCryptoAES.Mul8(a, b: Integer): Integer;
begin
Result := 0;
while b <> 0 do
begin
if b and 1 <> 0 then
Result := Result xor a;
b := b shr 1;
a := a shl 1;
if a and $100 <> 0 then
a := a xor $11b;
end;
end;
procedure TCryptoAES.Apply(var a: TCryptoAESMatrix; const Box: TCryptoAESSBox);
var
i: Integer;
begin
for i := 0 to 15 do
a[i] := Box[a[i]]
end;
procedure TCryptoAES.Diffuse(var a: TCryptoAESMatrix; const DiffuseRow: TCryptoAESDiffuseRow);
var
i, j, k: Integer;
Col: array[0..3] of Byte;
begin
for j := 0 to 3 do
begin
for i := 0 to 3 do
Col[i] := 0;
for i := 0 to 3 do
for k := 0 to 3 do
Col[i] := Col[i] xor Mul8(DiffuseRow[(k - i) and 3], a[k*4 + j]);
for i := 0 to 3 do
a[i*4 + j] := Col[i];
end;
end;
procedure TCryptoAES.ExpandKey(const Key: array of Byte);
var
i, j, Nk: Integer;
c: Byte; // 2^(i/Nk - 1) in GF(2^8)
a: array of array[0..3] of Byte;
t: array[0..3] of Byte;
begin
Nk := Length(Key) div 4;
SetLength(a, 4*(FNr + 1));
for i := 0 to Nk - 1 do
for j := 0 to 3 do
a[i][j] := Key[i*4 + j];
c := 1;
for i := Nk to 4*(FNr + 1) - 1 do
begin
for j := 0 to 3 do
t[j] := a[i - 1][j];
if i mod Nk = 0 then
begin
for j := 0 to 3 do
t[j] := AESSBox[a[i - 1][(j + 1) and 3]];
t[0] := t[0] xor c;
c := Mul8(c, 2);
end;
if (i mod Nk = 4) and (Nk > 6) then
for j := 0 to 3 do
t[j] := AESSBox[a[i - 1][j]];
for j := 0 to 3 do
a[i][j] := t[j] xor a[i - Nk][j];
end;
SetLength(FW, FNr + 1);
for i := 0 to 4*(FNr + 1) - 1 do
for j := 0 to 3 do
FW[i div 4][j*4 + i mod 4] := a[i][j]
end;
procedure TCryptoAES.Rotate(var a: TCryptoAESMatrix; Dir: Integer);
var
i, j: Integer;
r: array[0..3] of Byte;
begin
for i := 0 to 3 do
begin
for j := 0 to 3 do
r[j] := a[i*4 + (j - Dir*i) mod 4];
for j := 0 to 3 do
a[i*4 + j] := r[j];
end;
end;
procedure TCryptoAES.Sum(var a: TCryptoAESMatrix; const b, c: TCryptoAESMatrix);
var
i: Integer;
begin
for i := 0 to 15 do
a[i] := b[i] xor c[i]
end;
procedure TCryptoAES.Mov(var a: TCryptoAESMatrix; const b: TCryptoAESMatrix);
var
i: Integer;
begin
for i := 0 to 15 do
a[i] := b[i]
end;
procedure TCryptoAES.Zero(var a: TCryptoAESMatrix);
var
i: Integer;
begin
for i := 0 to 15 do
a[i] := 0
end;
procedure TCryptoAES.Encrypt(var a: TCryptoAESMatrix);
const
R: TCryptoAESDiffuseRow = (2, 3, 1, 1);
var
i: Integer;
begin
Sum(a, a, FW[0]);
for i := 1 to FNr - 1 do
begin
Apply(a, AESSBox);
Rotate(a, -1);
Diffuse(a, R);
Sum(a, a, FW[i]);
end;
Apply(a, AESSBox);
Rotate(a, -1);
Sum(a, a, FW[FNr]);
end;
procedure TCryptoAES.Decrypt(var a: TCryptoAESMatrix);
const
R: TCryptoAESDiffuseRow = ($e, $b, $d, 9);
var
i: Integer;
begin
Sum(a, a, FW[FNr]);
for i := FNr - 1 downto 1 do
begin
Rotate(a, +1);
Apply(a, AESIBox);
Sum(a, a, FW[i]);
Diffuse(a, R);
end;
Rotate(a, +1);
Apply(a, AESIBox);
Sum(a, a, FW[0]);
end;
class procedure TCryptoAES.MatrixToData(out Data; const a: TCryptoAESMatrix);
var
i, j: Integer;
begin
for i := 0 to 3 do
for j := 0 to 3 do
SetNBytesAt(Data, j*4 + i, 1, a[i*4 + j])
end;
class procedure TCryptoAES.DataToMatrix(out a: TCryptoAESMatrix; const Data);
var
i, j: Integer;
begin
for i := 0 to 3 do
for j := 0 to 3 do
a[i*4 + j] := GetNBytesAt(Data, j*4 + i, 1)
end;
procedure TCryptoAES.Decrypt(var Output; const Input);
var
a: TCryptoAESMatrix;
begin
DataToMatrix(a, Input);
Decrypt(a);
MatrixToData(Output, a);
end;
procedure TCryptoAES.Encrypt(var Output; const Input);
var
a: TCryptoAESMatrix;
begin
DataToMatrix(a, Input);
Encrypt(a);
MatrixToData(Output, a);
end;
constructor TCryptoAES.Create(const Key: array of Byte);
begin
case Length(Key) of
4*4: FNr := 10;
4*6: FNr := 12;
4*8: FNr := 14;
else raise ECryptoAESInvalidKeyLength.CreateFmt('AES-%d is undefined',
[Length(Key)*8]);
end;
ExpandKey(Key);
end;
{ TCryptoAES_CBC }
constructor TCryptoAES_CBC.Create(const Key: array of Byte; const IV);
begin
inherited Create(Key);
DataToMatrix(FC, IV);
end;
procedure TCryptoAES_CBC.Encrypt(var a: TCryptoAESMatrix);
begin
Sum(a, a, FC);
inherited Encrypt(a);
Mov(FC, a);
end;
procedure TCryptoAES_CBC.Decrypt(var a: TCryptoAESMatrix);
var
s: TCryptoAESMatrix;
begin
Mov(s, FC);
Mov(FC, a);
inherited Decrypt(a);
Sum(a, a, s);
end;
{ ECryptoHashUnknown }
constructor ECryptoHashUnknown.Create(Name: string);
begin
CreateFmt('Hash function %s is unknown', [Name]);
end;
{ TCryptoJenkins }
constructor TCryptoJenkins.Create;
begin
inherited Create(64)
end;
function TCryptoJenkins.DigestSize: Integer;
begin
Result := 4
end;
procedure TCryptoJenkins.Finish(LengthSize: Integer; Straight: Boolean);
var
h: Cardinal;
begin
inherited;
h := FState;
h := h + (h shl 3);
h := h xor (h shr 11);
h := h + (h shl 15);
FState := h;
end;
procedure TCryptoJenkins.GetDigest(out Digest; Size: Integer);
begin
Assert(Size = DigestSize);
Finish(ChunkSize div 2, True);
Move(FState, Digest, Size);
end;
procedure TCryptoJenkins.Process(const Chunk: array of Byte);
var
h: Cardinal;
i: Integer;
begin
h := FState;
for i := 0 to ChunkSize - 1 do
begin
h := h + Chunk[i];
h := h + (h shl 10);
h := h xor (h shr 6);
end;
FState := h;
end;
procedure TCryptoJenkins.Reset;
begin
inherited;
FState := 0;
end;
initialization
TCryptoMD5.InitSinTable;
TCryptoWhirlpool.InitSBox;
TCryptoWhirlpool.InitMul8;
TCryptoAES.InitSBox;
end.