Fully Implemented AES Encryption in Delphi: Mam.AES.pas with OpenSSL‑Compatible CBC, CTR, and ECB Modes
This page introduces Mam.AES.pas, a Delphi unit that provides complete AES encryption and decryption functionality.
It is a pure‑Delphi implementation requiring no external DLLs, supporting AES‑128/192/256 in CBC, CTR, and ECB modes, with OpenSSL‑compatible salt generation and built‑in key/IV hashing.
The unit also includes practical features such as Base64 encoding, round‑key generation, and proper handling of initialization vectors—covering everything needed for real‑world encryption workflows.
Usage
The full source code for Mam.AES.pas is provided at the bottom of this page.
Save it as Mam.AES.pas and include it with:
uses Mam.AES;
(It should work on Delphi XE8 or later.)
The recommended modes are AES‑128‑CBC, AES‑192‑CBC, AES‑256‑CBC, AES‑128‑CTR, AES‑192‑CTR, and AES‑256‑CTR.
The ECB variants (AES‑128‑ECB, AES‑192‑ECB, AES‑256‑ECB) are not recommended.
It is also recommended to use a randomly generated salt value for key derivation.
| Block Size | Key Length | Initialization Vector (IV) | |
| AES‑128‑ECB | 128‑bit (16 bytes) | 128‑bit (16 bytes) | None |
| AES‑192‑ECB | 128‑bit (16 bytes) | 192‑bit (24 bytes) | None |
| AES‑256‑ECB | 128‑bit (16 bytes) | 256‑bit (32 bytes) | None |
| AES‑128‑CBC | 128‑bit (16 bytes) | 128‑bit (16 bytes) | 128‑bit (16 bytes) |
| AES‑192‑CBC | 128‑bit (16 bytes) | 192‑bit (24 bytes) | 128‑bit (16 bytes) |
| AES‑256‑CBC | 128‑bit (16 bytes) | 256‑bit (32 bytes) | 128‑bit (16 bytes) |
| AES‑128‑CTR | 128‑bit (16 bytes) | 128‑bit (16 bytes) | 128‑bit (16 bytes) |
| AES‑192‑CTR | 128‑bit (16 bytes) | 192‑bit (24 bytes) | 128‑bit (16 bytes) |
| AES‑256‑CTR | 128‑bit (16 bytes) | 256‑bit (32 bytes) | 128‑bit (16 bytes) |
(1) Usage Example — AES‑128‑ECB (Not Recommended)
When using AES in ECB mode, no initialization vector (IV) is used.
uses Mam.AES,
System.NetEncoding;
procedure TForm1.Button1Click(Sender: TObject);
var
aes: TMamAes;
data, password: String;
dec, key, enc: TBytes;
st: string;
begin
// Data to encrypt
data := 'This string will be encrypted';
// Encryption password (used directly as a 16‑byte key)
password := '1234567890123456';
// Prepare data for encryption (convert UTF‑8 string to byte array)
dec := TEncoding.UTF8.GetBytes(data);
key := TEncoding.ASCII.GetBytes(password); // Encryption key
aes := TMamAes.Create(TMamAesType.MamAES128ECB);
// Perform AES‑128‑ECB encryption
enc := aes.Encrypt(dec, key);
// Perform AES‑128‑ECB decryption
dec := aes.Decrypt(enc, key);
// Convert decrypted byte array back to UTF‑8 string
st := TEncoding.UTF8.GetString(dec);
ShowMessage(st);
aes.Free;
end;
(2) Usage Example — AES‑192‑CBC
This example demonstrates AES‑192‑CBC encryption and decryption using the same key‑derivation method as OpenSSL.
A random salt value is generated, and both the key and IV are derived from password + salt using MD5 hashing.
The functions OpenSslEncrypt and OpenSslDecrypt handle this process automatically.
uses Mam.AES,
System.NetEncoding;
procedure TForm1.Button1Click(Sender: TObject);
var
aes: TMamAes;
data, password: String;
dec, enc: TBytes;
st: string;
begin
aes := TMamAes.Create(TMamAesType.MamAES192CBC);
// UTF‑8 text to encrypt (Japanese text used here as an example)
data := 'This string will be encrypted';
dec := TEncoding.UTF8.GetBytes(data);
password := 'abcdefghijklmnop';
// Perform AES encryption (OpenSSL‑compatible key/IV derivation)
enc := aes.OpenSslEncrypt(dec, password);
// Perform AES decryption
dec := aes.OpenSslDecrypt(enc, password);
// Convert decrypted bytes back to UTF‑8 string
st := TEncoding.UTF8.GetString(dec);
ShowMessage(st);
aes.Free;
end;
Mam.AES.pas
unit Mam.AES;
interface
uses Winapi.Windows,system.SysUtils;
type
TMamAesType=(
MamAES128ECB,MamAES192ECB,MamAES256ECB,
MamAES128CBC,MamAES192CBC,MamAES256CBC,
MamAES128CTR,MamAES192CTR,MamAES256CTR
);
TAesMatrix44=array[0..3] of array[0..3]of byte;
PAesMatrix44=^TAesMatrix44;
TMamAes = class(TObject)
private
fAesType: TMamAesType;
fKeyNum: Byte; // Key length
fKey: TBytes; // Key bytes (128, 192, or 256 bits)
fIV: TBytes; // Initialization Vector for CBC/CTR (fixed 128-bit)
fNumOfRound: Byte; // Number of AES rounds required
//fRound: Byte; // Current round (unused)
fNk: Byte; // Key length in units of Nb (4-byte words)
fRoundKey: TBytes; // Expanded round keys
fData: TBytes; // Data buffer for encryption/decryption
fIsECB: Boolean; // ECB mode flag
fIsCBC: Boolean; // CBC mode flag
fIsCTR: Boolean; // CTR mode flag
// Generate round keys (Key Expansion)
procedure KeyExpansion();
// Set initialization vector (IV)
procedure SetIv(iv: TBytes);
// Set AES mode (ECB128, CBC128, etc.)
procedure SetAesType(AesType: TMamAesType);
// XOR with IV (for CBC/CTR)
procedure XorIV(buf: PByte);
procedure Cipher(State: PAesMatrix44);
procedure InvCipher(State: PAesMatrix44);
procedure AddRoundKey(Round: Byte; State: PAesMatrix44);
procedure SubBytes(State: PAesMatrix44);
procedure InvSubBytes(State: PAesMatrix44);
procedure ShiftRows(State: PAesMatrix44);
procedure InvShiftRows(State: PAesMatrix44);
procedure MixColumns(State: PAesMatrix44);
procedure InvMixColumns(State: PAesMatrix44);
function Multiply(x, y: Byte): Byte;
function XTime(x: Byte): Byte;
public
// Constructor
constructor Create(AesType: TMamAesType);
// Encryption
function Encrypt(data, key: TBytes): TBytes; overload;
function Encrypt(data, key, iv: TBytes): TBytes; overload;
// Decryption
function Decrypt(data, key: TBytes): TBytes; overload;
function Decrypt(data, key, iv: TBytes): TBytes; overload;
// Base64 Encode
function Base64Encode(data: TBytes): String;
// Base64 Decode
function Base64Decode(data: String): TBytes;
// OpenSSL‑compatible encryption:
// - Generate random salt
// - Derive key from password + salt using MD5
// - Derive IV using MD5
// - Perform AES encryption/decryption
function OpenSslEncrypt(data: TBytes; Password: String): TBytes;
function OpenSslDecrypt(data: TBytes; Password: String): TBytes;
end;
implementation
uses System.Hash,
System.NetEncoding;
const
// Number of AES columns (encryption operates on 4-byte words)
Nb: Byte = 4;
// AES block size (fixed 128-bit = 16 bytes)
AES_BlockSize: Byte = 16;
const
SBox:array[0..255]of byte=(
$63, $7c, $77, $7b, $f2, $6b, $6f, $c5, $30, $01, $67, $2b, $fe, $d7, $ab, $76,
$ca, $82, $c9, $7d, $fa, $59, $47, $f0, $ad, $d4, $a2, $af, $9c, $a4, $72, $c0,
$b7, $fd, $93, $26, $36, $3f, $f7, $cc, $34, $a5, $e5, $f1, $71, $d8, $31, $15,
$04, $c7, $23, $c3, $18, $96, $05, $9a, $07, $12, $80, $e2, $eb, $27, $b2, $75,
$09, $83, $2c, $1a, $1b, $6e, $5a, $a0, $52, $3b, $d6, $b3, $29, $e3, $2f, $84,
$53, $d1, $00, $ed, $20, $fc, $b1, $5b, $6a, $cb, $be, $39, $4a, $4c, $58, $cf,
$d0, $ef, $aa, $fb, $43, $4d, $33, $85, $45, $f9, $02, $7f, $50, $3c, $9f, $a8,
$51, $a3, $40, $8f, $92, $9d, $38, $f5, $bc, $b6, $da, $21, $10, $ff, $f3, $d2,
$cd, $0c, $13, $ec, $5f, $97, $44, $17, $c4, $a7, $7e, $3d, $64, $5d, $19, $73,
$60, $81, $4f, $dc, $22, $2a, $90, $88, $46, $ee, $b8, $14, $de, $5e, $0b, $db,
$e0, $32, $3a, $0a, $49, $06, $24, $5c, $c2, $d3, $ac, $62, $91, $95, $e4, $79,
$e7, $c8, $37, $6d, $8d, $d5, $4e, $a9, $6c, $56, $f4, $ea, $65, $7a, $ae, $08,
$ba, $78, $25, $2e, $1c, $a6, $b4, $c6, $e8, $dd, $74, $1f, $4b, $bd, $8b, $8a,
$70, $3e, $b5, $66, $48, $03, $f6, $0e, $61, $35, $57, $b9, $86, $c1, $1d, $9e,
$e1, $f8, $98, $11, $69, $d9, $8e, $94, $9b, $1e, $87, $e9, $ce, $55, $28, $df,
$8c, $a1, $89, $0d, $bf, $e6, $42, $68, $41, $99, $2d, $0f, $b0, $54, $bb, $16
);
InvSBox:array[0..255]of byte=(
$52, $09, $6a, $d5, $30, $36, $a5, $38, $bf, $40, $a3, $9e, $81, $f3, $d7, $fb,
$7c, $e3, $39, $82, $9b, $2f, $ff, $87, $34, $8e, $43, $44, $c4, $de, $e9, $cb,
$54, $7b, $94, $32, $a6, $c2, $23, $3d, $ee, $4c, $95, $0b, $42, $fa, $c3, $4e,
$08, $2e, $a1, $66, $28, $d9, $24, $b2, $76, $5b, $a2, $49, $6d, $8b, $d1, $25,
$72, $f8, $f6, $64, $86, $68, $98, $16, $d4, $a4, $5c, $cc, $5d, $65, $b6, $92,
$6c, $70, $48, $50, $fd, $ed, $b9, $da, $5e, $15, $46, $57, $a7, $8d, $9d, $84,
$90, $d8, $ab, $00, $8c, $bc, $d3, $0a, $f7, $e4, $58, $05, $b8, $b3, $45, $06,
$d0, $2c, $1e, $8f, $ca, $3f, $0f, $02, $c1, $af, $bd, $03, $01, $13, $8a, $6b,
$3a, $91, $11, $41, $4f, $67, $dc, $ea, $97, $f2, $cf, $ce, $f0, $b4, $e6, $73,
$96, $ac, $74, $22, $e7, $ad, $35, $85, $e2, $f9, $37, $e8, $1c, $75, $df, $6e,
$47, $f1, $1a, $71, $1d, $29, $c5, $89, $6f, $b7, $62, $0e, $aa, $18, $be, $1b,
$fc, $56, $3e, $4b, $c6, $d2, $79, $20, $9a, $db, $c0, $fe, $78, $cd, $5a, $f4,
$1f, $dd, $a8, $33, $88, $07, $c7, $31, $b1, $12, $10, $59, $27, $80, $ec, $5f,
$60, $51, $7f, $a9, $19, $b5, $4a, $0d, $2d, $e5, $7a, $9f, $93, $c9, $9c, $ef,
$a0, $e0, $3b, $4d, $ae, $2a, $f5, $b0, $c8, $eb, $bb, $3c, $83, $53, $99, $61,
$17, $2b, $04, $7e, $ba, $77, $d6, $26, $e1, $69, $14, $63, $55, $21, $0c, $7d
);
RCon:array[0..10]of byte=(
$8d, $01, $02, $04, $08, $10, $20, $40, $80, $1b, $36);
MamBase64: String =
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'+
'abcdefghijklmnopqrstuvwxyz'+
'0123456789+/';
{ TMamAes }
procedure TMamAes.AddRoundKey(Round: Byte; State: PAesMatrix44);
var i,j:Cardinal;
begin
for i := 0 to 3 do
for j := 0 to 3 do
state^[i][j]:=State^[i][j] xor fRoundKey[(Round*Nb*4)+(i*Nb)+j];
end;
function TMamAes.Base64Decode(data:String):TBytes;
var i,Len:integer;
b:byte;
i1,i2,i3,i4:integer;
begin
SetLength(Result,0);
i:=0;
Len:=length(data);
while i<Len do
begin
i1:=Pos(data.Substring(i,1),MamBase64)-1;
inc(i);
i2:=Pos(data.Substring(i,1),MamBase64)-1;
inc(i);
i3:=Pos(data.Substring(i,1),MamBase64)-1;
inc(i);
i4:=Pos(data.Substring(i,1),MamBase64)-1;
if i3=-1 then
begin
b:=(i1 shl 2)+((i2 and $30) shr 4);
SetLength(Result,Length(Result)+1);
Result[Length(Result)-1]:=b;
end
else if i4=-1 then
begin
b:=(i1 shl 2)+((i2 and $30) shr 4);
SetLength(Result,Length(Result)+1);
Result[Length(Result)-1]:=b;
b:=((i2 and $0f) shl 4) + ((i3 and $3c) shr 2);
SetLength(Result,Length(Result)+1);
Result[Length(Result)-1]:=b;
end
else
begin
b:=(i1 shl 2)+((i2 and $30) shr 4);
SetLength(Result,Length(Result)+1);
Result[Length(Result)-1]:=b;
b:=((i2 and $0f) shl 4) + ((i3 and $3c) shr 2);
SetLength(Result,Length(Result)+1);
Result[Length(Result)-1]:=b;
b:=((i3 and $03) shl 6)+ (i4 and $3f);
SetLength(Result,Length(Result)+1);
Result[Length(Result)-1]:=b;
end;
inc(i);
end;
end;
function TMamAes.Base64Encode(data: TBytes): String;
var i,Len:Cardinal;
enc:array[0..3] of String;
b1,b2,b3:byte;
begin
Len:=Length(data);
result:='';
i:=0;
while i<Len do
begin
enc[0]:='';
enc[1]:='';
enc[2]:='';
enc[3]:='';
//ZeroMemory(@enc,4);
b1:=data[i];
enc[0]:=MamBase64.Substring((b1 and $fc) shr 2,1);
inc(i);
if i>=Len then
begin
enc[1]:=MamBase64.Substring((b1 and $3) shl 4,1);
enc[2]:='=';
enc[3]:='=';
end
else
begin
b2:=data[i];
enc[1]:=MamBase64.Substring(((b1 and $3) shl 4 )+((b2 and $f0) shr 4),1);
inc(i);
if i>=Len then
begin
enc[2]:=MamBase64.Substring((b2 and $f) shl 2,1);
enc[3]:='=';
end
else
begin
b3:=data[i];
enc[2]:=MamBase64.Substring(((b2 and $f) shl 2)+((b3 and $c0) shr 6),1);
enc[3]:=MamBase64.Substring(b3 and $3f,1);
end;
end;
Result:=Result+enc[0]+enc[1]+enc[2]+enc[3];
inc(i);
end;
end;
procedure TMamAes.Cipher(State: PAesMatrix44);
var Round:Byte;
begin
Round:=0;
AddRoundKey(Round,State);
inc(round);
while true do
begin
SubBytes(State);
shiftrows(State);
if Round>=fNumOfRound then break;
mixcolumns(State);
AddRoundKey(Round,State);
inc(Round);
end;
AddRoundKey(Round,State);
end;
procedure TMamAes.InvCipher(State: PAesMatrix44);
var Round:byte;
begin
AddRoundKey(fNumOfRound,State);
Round:=fNumOfRound-1;
while true do
begin
InvShiftRows(State);
InvSubBytes(State);
AddRoundKey(Round,State);
if round=0 then break;
InvMixColumns(state);
dec(Round);
end;
end;
constructor TMamAes.Create(AesType:TMamAesType);
//Constructor
begin
inherited Create();
setLength(fIV,AES_BlockSize);
ZeroMemory(fIV,AES_BlockSize);
SetAesType(AesType);
end;
function TMamAes.Decrypt(data, key: TBytes):TBytes;
var i,len,bi:Cardinal;
NextIv:TBytes;
buf:TBytes;
begin
//Set encryption key
len:=Length(key);
SetLength(fkey,fKeyNum);
ZeroMemory(@fkey[0],fKeyNum);
if len>fKeyNum then len:=fKeyNum;
move(key[0],fkey[0],len);
KeyExpansion();
//Store data to be decrypted
len:=length(data);
SetLength(fData,len);
ZeroMemory(fData,len);
Move(data[0],fData[0],len);
if fIsECB or fIsCBC then
begin
//AES‑ECB or AES‑CBC
setlength(NextIv,AES_BlockSize);
i:=0;
while i<len do
begin
move(fData[i],NextIv[0],AES_BlockSize);
invCipher(PAesMatrix44(@fData[i]));
if fIsCBC then
begin
XorIv(@fData[i]);
move(NextIv[0],fIV[0],AES_BlockSize);
end;
inc(i,AES_BlockSize);
end;
//PKCS#7
len:=len-fData[len-1];
setlength(fData,len);
end
else
begin
//AES-CTR mode
i:=0;
bi:=AES_BlockSize;
setlength(buf,AES_BlockSize);
while i<len do
begin
if bi=AES_BlockSize then
begin
Move(fIV[0],buf[0],AES_BlockSize);
Cipher(PAesMatrix44(@buf[0]));
for bi := (AES_BlockSize-1) downto 0 do
begin
if (fIV[bi]=255) then
begin
fIV[bi]:=0;
end
else
begin
inc(fIV[bi]);
break;
end;
end;
bi:=0;
end;
fData[i] := (fData[i] xor buf[bi]);
inc(i);
inc(bi);
end;
end;
Result:=fData;
end;
function TMamAes.Decrypt(data, key, iv: TBytes):TBytes;
begin
SetIv(iv);
Result:=Decrypt(data,key);
end;
function TMamAes.Encrypt(data, key: TBytes):TBytes;
var i,bi:Cardinal;
Len:Cardinal;
LenPKCS7:Cardinal;
buf:TBytes;
begin
//Set encryption key
Len:=Length(key);
SetLength(fkey,fKeyNum);
ZeroMemory(@fkey[0],fKeyNum);
if Len>fKeyNum then Len:=fKeyNum;
move(key[0],fkey[0],Len);
KeyExpansion();
//Store data to be encrypted
Len:=length(data);
SetLength(fData,Len);
ZeroMemory(fData,Len);
Move(data[0],fData[0],Len);
if fIsECB or fIsCBC then
begin
//AES-ECB or AES-CBC
//PKCS#7
LenPKCS7:=(Len div 16 * 16)+16;
setlength(fData,LenPKCS7);
for i := Len to LenPKCS7-1 do
fData[i]:=byte(LenPKCS7-Len);
Len:=LenPKCS7;
i:=0;
while i<Len do
begin
if fIsCBC then self.XorIV(@fData[i]);
self.Cipher(PAesMatrix44(@fData[i]));
if fIsCBC then
Move(fData[i],fIV[0],AES_BlockSize);
Inc(i,AES_BlockSize);
end;
end
else
begin
//AES-CTR mode
i:=0;
bi:=AES_BlockSize;
setlength(buf,AES_BlockSize);
while i<len do
begin
if bi=AES_BlockSize then
begin
Move(fIV[0],buf[0],AES_BlockSize);
Cipher(PAesMatrix44(@buf[0]));
for bi := (AES_BlockSize-1) downto 0 do
begin
if (fIV[bi]=255) then
begin
fIV[bi]:=0;
end
else
begin
inc(fIV[bi]);
break;
end;
end;
bi:=0;
end;
fData[i] := (fData[i] xor buf[bi]);
inc(i);
inc(bi);
end;
end;
result:=fData;
end;
function TMamAes.Encrypt(data, key, iv: TBytes):TBytes;
begin
self.SetIv(iv);
result:=self.Encrypt(data,key);
end;
procedure TMamAes.KeyExpansion;
var AES_keyExpSize:Cardinal;
i,j,k:Cardinal;
tmp:array[0..3] of Byte;
b:Byte;
begin
AES_keyExpSize:=176+32*0;
if fNumOfRound=10 then AES_keyExpSize:=176+32*0;
if fNumOfRound=12 then AES_keyExpSize:=176+32*1;
if fNumOfRound=14 then AES_keyExpSize:=176+32*2;
SetLength(fRoundKey,AES_keyExpSize);
for i := 0 to fNk-1 do
begin
fRoundKey[i*4+0]:=fKey[i*4+0];
fRoundKey[i*4+1]:=fKey[i*4+1];
fRoundKey[i*4+2]:=fKey[i*4+2];
fRoundKey[i*4+3]:=fKey[i*4+3];
end;
for i := fNk to Nb*(fNumOfRound+1)-1 do
begin
k:=(i-1)*4;
tmp[0]:=fRoundKey[k+0];
tmp[1]:=fRoundKey[k+1];
tmp[2]:=fRoundKey[k+2];
tmp[3]:=fRoundKey[k+3];
if (i mod fNk)=0 then
begin
b:=tmp[0];
tmp[0]:=tmp[1];
tmp[1]:=tmp[2];
tmp[2]:=tmp[3];
tmp[3]:=b;
tmp[0] := sbox[tmp[0]];
tmp[1] := sbox[tmp[1]];
tmp[2] := sbox[tmp[2]];
tmp[3] := sbox[tmp[3]];
tmp[0]:=tmp[0] xor Rcon[i div fNk];
end;
if fNumOfRound=14 then
begin //aes256
if (i mod fNk)=4 then
begin
tmp[0] := sbox[tmp[0]];
tmp[1] := sbox[tmp[1]];
tmp[2] := sbox[tmp[2]];
tmp[3] := sbox[tmp[3]];
end;
end;
j:=i*4;
k:=(i-fNk)*4;
fRoundKey[j+0]:=fRoundKey[k+0] xor tmp[0];
fRoundKey[j+1]:=fRoundKey[k+1] xor tmp[1];
fRoundKey[j+2]:=fRoundKey[k+2] xor tmp[2];
fRoundKey[j+3]:=fRoundKey[k+3] xor tmp[3];
end;
end;
procedure TMamAes.MixColumns(State: PAesMatrix44);
var i:integer;
tmp,tmp1,tmp2:byte;
begin
for i := 0 to 3 do
begin
tmp :=State^[i][0];
tmp1:=State^[i][0] xor State^[i][1] xor
State^[i][2] xor State^[i][3];
tmp2:=State^[i][0] xor State^[i][1];
tmp2:=XTime(tmp2);
State^[i][0]:= State^[i][0] xor tmp2 xor tmp1;
tmp2:=State^[i][1] xor State^[i][2];
tmp2:=XTime(tmp2);
State^[i][1]:= State^[i][1] xor tmp2 xor tmp1;
tmp2:=State^[i][2] xor State^[i][3];
tmp2:=XTime(tmp2);
State^[i][2]:= State^[i][2] xor tmp2 xor tmp1;
tmp2:=State^[i][3] xor tmp;
tmp2:=XTime(tmp2);
State^[i][3]:= State^[i][3] xor tmp2 xor tmp1;
end;
end;
procedure TMamAes.InvMixColumns(State: PAesMatrix44);
var i:Cardinal;
tmp1,tmp2,tmp3,tmp4:byte;
begin
for i := 0 to 3 do
begin
tmp1:=State^[i][0];
tmp2:=State^[i][1];
tmp3:=State^[i][2];
tmp4:=State^[i][3];
State^[i][0]:=
Multiply(tmp1,$0e) xor Multiply(tmp2,$0b) xor
Multiply(tmp3,$0d) xor Multiply(tmp4,$09);
State^[i][1]:=
Multiply(tmp1,$09) xor Multiply(tmp2,$0e) xor
Multiply(tmp3,$0b) xor Multiply(tmp4,$0d);
State^[i][2]:=
Multiply(tmp1,$0d) xor Multiply(tmp2,$09) xor
Multiply(tmp3,$0e) xor Multiply(tmp4,$0b);
State^[i][3]:=
Multiply(tmp1,$0b) xor Multiply(tmp2,$0d) xor
Multiply(tmp3,$09) xor Multiply(tmp4,$0e);
end;
end;
procedure TMamAes.SetAesType(AesType: TMamAesType);
//Set AES mode (ECB, CBC, CTR)
begin
fIsECB:=False;
fIsCBC:=False;
fIsCTR:=False;
//Set mode flags
fAesType:=AesType;
if (fAesType=MamAES128ECB) or
(fAesType=MamAES128CBC) or
(fAesType=MamAES128CTR) then
fKeyNum:=16
else if (fAesType=MamAES192ECB) or
(fAesType=MamAES192CBC) or
(fAesType=MamAES192CTR) then
fKeyNum:=24
else
fKeyNum:=32;
if (fAesType=MamAES128ECB) or
(fAesType=MamAES192ECB) or
(fAesType=MamAES256ECB) then fIsECB:=True;
if (fAesType=MamAES128CBC) or
(fAesType=MamAES192CBC) or
(fAesType=MamAES256CBC) then fIsCBC:=True;
if (fAesType=MamAES128CTR) or
(fAesType=MamAES192CTR) or
(fAesType=MamAES256CTR) then fIsCTR:=True;
fNk:=fKeyNum div Nb;//number of 32‑bit words in the key
fNumOfRound:=fNk+6; //Number of AES rounds
end;
procedure TMamAes.SetIv(iv: TBytes);
var len:Cardinal;
//Set initialization vector (IV)
begin
setLength(fIV,AES_BlockSize);
ZeroMemory(@fIV[0],AES_BlockSize);
len:=length(iv);
if len>AES_BlockSize then len:=AES_BlockSize;
Move(iv[0],fIV[0],len);
end;
procedure TMamAes.ShiftRows(State: PAesMatrix44);
var tmp:Byte;
begin
tmp :=State^[0][1];
State^[0][1]:=State^[1][1];
State^[1][1]:=State^[2][1];
State^[2][1]:=State^[3][1];
State^[3][1]:=tmp;
tmp :=State^[0][2];
State^[0][2]:=State^[2][2];
State^[2][2]:=tmp;
tmp :=State^[1][2];
State^[1][2]:=State^[3][2];
State^[3][2]:=tmp;
tmp :=State^[0][3];
State^[0][3]:=State^[3][3];
State^[3][3]:=State^[2][3];
State^[2][3]:=State^[1][3];
State^[1][3]:=tmp;
end;
procedure TMamAes.InvShiftRows(State: PAesMatrix44);
var tmp:Byte;
begin
tmp :=state^[3][1];
state^[3][1]:=state^[2][1];
state^[2][1]:=state^[1][1];
state^[1][1]:=state^[0][1];
state^[0][1]:=tmp;
tmp :=state^[0][2];
state^[0][2]:=state^[2][2];
state^[2][2]:=tmp;
tmp :=state^[1][2];
state^[1][2]:=state^[3][2];
state^[3][2]:=tmp;
tmp :=state^[0][3];
state^[0][3]:=state^[1][3];
state^[1][3]:=state^[2][3];
state^[2][3]:=state^[3][3];
state^[3][3]:=tmp;
end;
procedure TMamAes.SubBytes(State: PAesMatrix44);
var i,j:integer;
begin
for i := 0 to 3 do
for j := 0 to 3 do
State^[j][i]:= SBox[State^[j][i]];
end;
procedure TMamAes.InvSubBytes(State: PAesMatrix44);
var i,j:integer;
begin
for i := 0 to 3 do
for j := 0 to 3 do
State^[j][i]:= InvSBox[State^[j][i]];
end;
procedure TMamAes.XorIV(buf: PByte);
var i:Cardinal;
begin
for i := 0 to AES_BlockSize-1 do
begin
buf^:=buf^ xor fIV[i];
inc(buf);
end;
end;
function TMamAes.Multiply(x, y: Byte): Byte;
begin
result:=(
((y and 1) * x) xor
(((y shr 1) and 1)*xtime(x)) xor
(((y shr 2) and 1)*xtime(xtime(x))) xor
(((y shr 3) and 1)*xtime(xtime(xtime(x)))) xor
(((y shr 4) and 1)*xtime(xtime(xtime(xtime(x)))))
);
end;
function TMamAes.OpenSslDecrypt(data: TBytes; Password: String): TBytes;
var b1,b2,b3:TBytes;
pass,salted,salt,iv,pass_salt,key:TBytes;
i:Cardinal;
flag:boolean;
dec:TBytes;
md5:THashMD5;
data_nosalt:TBytes;
begin
// 'Salted__' + 8‑byte salt
// key = MD5(password + salt)
// IV = MD5(key + password + salt)
result:=dec;
//Minimum length check (except for CTR mode)
if not ((fAesType=TMamAesType.MamAES128CTR) or
(fAesType=TMamAesType.MamAES192CTR) or
(fAesType=TMamAesType.MamAES256CTR)) then
begin
if length(data)<32 then exit;
end;
//Validate and extract salt
salted:=TEncoding.ASCII.GetBytes('Salted__');
flag:=false;
for i := 0 to 7 do
if data[i]<>salted[i] then flag:=true;
if flag then exit;
setlength(salt,8);
move(data[8],salt[0],8);
// Remove 'Salted__' + salt header
setlength(data_nosalt,length(data)-16);
move(data[16],data_nosalt[0],length(data)-16);
//Convert password to byte array
pass:=TEncoding.ASCII.GetBytes(Password);
//Build pass + salt byte array
SetLength(pass_salt,Length(pass)+Length(salt));
Move(pass[0],pass_salt[0],Length(pass));
Move(salt[0],pass_salt[Length(pass)],Length(salt));
md5:=THashMD5.Create;
md5.Reset;
md5.Update(pass_salt);
b1:=md5.HashAsBytes;
md5.Reset;
setlength(b2,16+Length(pass_salt));
move(b1[0],b2[0],length(b1));
move(pass_salt[0],b2[length(b1)],length(pass_salt));
md5.Update(b2);
b2:=md5.HashAsBytes;
md5.Reset;
setlength(b3,16+Length(pass_salt));
move(b2[0],b3[0],length(b2));
move(pass_salt[0],b3[length(b2)],length(pass_salt));
md5.Update(b3);
b3:=md5.HashAsBytes;
setlength(iv,16);
if fKeyNum=16 then
begin
setlength(key,16);
move(b1[0],key[0],16);
move(b2[0],iv[0],16);
end
else if fKeyNum=24 then
begin
setlength(key,24);
move(b1[0],key[0],16);
move(b2[0],key[16],8);
move(b2[8],iv[0],8);
move(b3[0],iv[8],8);
end
else
begin
setlength(key,32);
move(b1[0],key[0],16);
move(b2[0],key[16],16);
move(b3[0],iv[0],16);
end;
dec:=self.Decrypt(data_nosalt,key,iv);
result:=dec;
end;
function TMamAes.OpenSslEncrypt(data: TBytes; Password: String): TBytes;
var i:Cardinal;
md5:THashMD5; //Available in Delphi XE8 and later
salt,pass,pass_salt:TBytes;
b1,b2,b3:TBytes;
key,iv:TBytes;
enc:TBytes;
salted:TBytes;
res:TBytes;
begin
// 'Salted__' + 8‑byte salt
// key = MD5(password + salt)
// IV = MD5(key + password + salt)
pass:=TEncoding.ASCII.GetBytes(Password);
//Generate random 8‑byte salt
Randomize;
SetLength(salt,8);
for i := 0 to 7 do
salt[i]:=Random(256);
//Build pass + salt byte array
SetLength(pass_salt,Length(pass)+Length(salt));
Move(pass[0],pass_salt[0],Length(pass));
Move(salt[0],pass_salt[Length(pass)],Length(salt));
md5:=THashMD5.Create;
md5.Reset;
md5.Update(pass_salt);
b1:=md5.HashAsBytes;
md5.Reset;
setlength(b2,16+Length(pass_salt));
move(b1[0],b2[0],length(b1));
move(pass_salt[0],b2[length(b1)],length(pass_salt));
md5.Update(b2);
b2:=md5.HashAsBytes;
md5.Reset;
setlength(b3,16+Length(pass_salt));
move(b2[0],b3[0],length(b2));
move(pass_salt[0],b3[length(b2)],length(pass_salt));
md5.Update(b3);
b3:=md5.HashAsBytes;
// Build key and IV depending on AES key size
setlength(iv,16);
if fKeyNum=16 then //AES-128
begin
setlength(key,16);
move(b1[0],key[0],16);
move(b2[0],iv[0],16);
end
else if fKeyNum=24 then //AES-192
begin
setlength(key,24);
move(b1[0],key[0],16);
move(b2[0],key[16],8);
move(b2[8],iv[0],8);
move(b3[0],iv[8],8);
end
else //AES-256
begin
setlength(key,32);
move(b1[0],key[0],16);
move(b2[0],key[16],16);
move(b3[0],iv[0],16);
end;
// Perform AES encryption
enc:=self.Encrypt(data,key,iv);
// Build OpenSSL‑compatible output: 'Salted__' + salt + encrypted data
salted:=TEncoding.ASCII.GetBytes('Salted__');
setlength(res,length(salted)+length(salt)+length(enc));
move(salted[0],res[0],length(salted));
move(salt[0],res[8],length(salt));
move(enc[0],res[16],length(enc));
result:=res;
end;
function TMamAes.XTime(x: byte): byte;
begin
result:=((x shl 1) xor (((x shr 7) and 1) * $1b));
end;
end.
