unit Unit1;
interface
uses system.classes, system.SysUtils;
type TC4D=class
type Pbytes=^Tbytes;
private
class function PBKDF2(const pass, salt: tbytes; const count, kLen: Integer): tbytes;
class function CalcDigest(const text: tbytes): tbytes;
class function CalcHMAC(const mess:tbytes; const key: tbytes):tbytes;
class function RPad(const x:tbytes; const c:byte; const s:Integer):tbytes;
class function XorBlock(const s, x: tbytes): tbytes;
class procedure mkpass(const password: utf8string; co,k:pbytes);
public
class function encrypt(const data:tbytes; const password: utf8string): tbytes;
class function decrypt(const data:tbytes; password: utf8string):tbytes;
end;
implementation
uses System.Hash, AESObj, miscobj;
{ TE4D }
class procedure TC4D.mkpass(const password: utf8string; co, k: Pbytes);
var
key: Tbytes;
n, i: Integer;
p: RawByteString;
begin
p := RawByteString(password);
setlength(key, length(p));
i := 0;
for n := low(p) to high(p) do
begin
key[i] := byte(password[n]);
inc(i);
end;
if length(co^) <> 8 then
begin
setlength(co^, 8);
for n := 0 to 7 do
co^[n] := random(256);
end;
setlength(k^, 16);
k^ := PBKDF2(key, co^, 1024, 16);
end;
class function TC4D.RPad(const x: Tbytes; const c: byte;
const s: Integer): Tbytes;
var
i, n: Integer;
begin
result := x;
n := length(x);
if n >= s then
exit;
setlength(result, s);
for i := n to s - 1 do
result[i] := c;
end;
class function TC4D.XorBlock(const s, x: Tbytes): Tbytes;
var
i: Integer;
begin
setlength(result, length(s));
for i := 0 to length(s) - 1 do
result[i] := s[i] xor x[i];
end;
class function TC4D.CalcDigest(const text: Tbytes): Tbytes;
var
v: THashSHA1;
begin
v.Reset;
v.Update(text);
result := v.HashAsBytes;
end;
class function TC4D.CalcHMAC(const mess: Tbytes; const key: Tbytes): Tbytes;
var
mkey, t: Tbytes;
const
blocksize = 64;
begin
if length(key) > blocksize then
mkey := CalcDigest(key)
else
mkey := key;
setlength(t, 0);
mkey := RPad(mkey, 0, blocksize);
result := XorBlock(mkey, RPad(t, $36, blocksize));
system.Insert(mess, result, length(result));
result := CalcDigest(result); // Definition RFC 2104
system.Insert(XorBlock(mkey, RPad(t, $5C, blocksize)), result, 0);
result := CalcDigest(result);
end;
class function TC4D.PBKDF2(const pass, salt: Tbytes;
const count, kLen: Integer): Tbytes;
var
J, i: Integer;
u, f, bsalt: Tbytes;
begin
setlength(result, 0);
i := length(salt);
bsalt := salt;
setlength(bsalt, i + 4);
bsalt[i] := 0;
bsalt[i + 1] := 0;
bsalt[i + 2] := 0;
bsalt[i + 3] := 1;
f := CalcHMAC(bsalt, pass);
u := f;
for J := 2 to count do
begin
u := CalcHMAC(u, pass);
f := XorBlock(f, u);
end;
system.Insert(f, result, length(result));
setlength(result, kLen);
end;
class function TC4D.encrypt(const data: Tbytes;
const password: utf8string): Tbytes;
var
key, salt, kk: Tbytes;
cif: TAESEncryption;
n, m: Integer;
i: cardinal;
si, so: tmemorystream;
cov: tconvert;
begin
result := nil;
i := length(data);
if (i = 0) or (length(password) < 8) then
exit;
try
m := 16 - (i mod 16);
if m = 0 then
m := 16;
setlength(key, m);
for n := 0 to m - 1 do
key[n] := m;
si := tmemorystream.Create;
so := tmemorystream.Create;
try
si.Write(data, i);
si.Write(key, m);
inc(i, m);
kk := nil;
mkpass(password, @kk, @key);
setlength(salt, 16);
for n := 0 to 15 do
salt[n] := random(256);
cov := tconvert.Create;
cif := TAESEncryption.Create(kl128, cov.tbytestostring(key), atCBC,
nopadding, raw, noUni, cov.tbytestostring(salt));
try
key := nil;
cif.EncryptStream(si, so);
if so.Size = 0 then
exit;
finally
cif.Free;
cov.Free;
end;
si.Clear;
so.Position := 0;
setlength(result, i + 24);
move(kk[0], result[0], 8);
move(salt[0], result[8], 16);
so.Read(result[24], i);
finally
si.Free;
so.Free;
end;
except
result := nil;
end;
end;
class function TC4D.decrypt(const data: Tbytes; password: utf8string): Tbytes;
var
cif: TAESEncryption;
key, salt: Tbytes;
i: cardinal;
si, so: tmemorystream;
cov: tconvert;
begin
result := nil;
if (length(data) < 25) or (length(password) < 8) then
exit;
try
setlength(salt, 8);
move(data[0], salt[0], 8);
mkpass(password, @salt, @key);
setlength(salt, 16);
move(data[8], salt[0], 16);
si := tmemorystream.Create;
so := tmemorystream.Create;
try
cov := tconvert.Create;
cif := TAESEncryption.Create(kl128, cov.tbytestostring(key), atCBC,
nopadding, raw, noUni, cov.tbytestostring(salt));
try
salt := nil;
key := nil;
si.Write(data[24], length(data) - 24);
if cif.DecryptStream(si, so) <> 0 then
exit;
finally
cif.Free;
cov.Free;
end;
si.Clear;
i := so.Size;
setlength(result, i);
so.Position := 0;
so.Read(result[0], i);
finally
si.Free;
so.Free;
end;
if result[i - 1] > 16 then
begin
result := nil;
exit;
end;
dec(i, result[i - 1]);
setlength(result, i);
except
result := nil;
end;
end;
end.