Jump to content
Soji

Delphi AES encryption/decryption

Recommended Posts

Hi,

 

I am looking for a Delphi AES encryption/decryption library to decrypt a text which is encrypted using power-shell script.

 

Another application encrypts a text using power-shell and put it into a file. My Delphi application has to read the file and decrypt the string. 

This is the power-shell script to encrypt ( Powershell encrypt and decrypt ). I want to mimic the function fAESDecrypt() in Delphi.

 

Anyone knows any libraries which can do this?

If there is nothing out there which can do it, I am thinking about executing the power-shell from Delphi to decrypt it. I think that should be possible.

 

Thanks in advance,

Soji.

Share this post


Link to post

@stijnsanders That is beauty, one small step though is missing though, and it will not help the OP in his question, no IV or block modes, and i don't think Soji can add those on his own, the most beautiful thing in your library is the clean code and uses clause.

 

@Ondrej Kelle I know CryptoLib4Pascal for long time but never used it, it is great, now i was browsing the code one thing caught my eye, i think the PKCS7 padding is wrong, as it does accept 0 byte padding and it should not, in other words you should never end up with a byte value 0 at the end, if the length of data is multiple of k then you go and add full block, i think the relation between these should be redesigned:

TPaddedBufferedBlockCipher.DoFinal  // does call AddPadding without handling the return value that is not a big deal, but FbufOff should be [1..k] and should never be 0, from https://tools.ietf.org/html/rfc5652#section-6.3

TPkcs7Padding.AddPadding // don't check if inOff is 0

 

Also i tried to run the tests and play with it, but that package is missing a file HlpIhash, either that or i am missing something.

Share this post


Link to post
4 minutes ago, Kas Ob. said:

no IV or block modes

@Kas Ob.: I spotted the same. Thanks for pointing it out.

 

5 minutes ago, Kas Ob. said:

that package is missing a file HlpIhash

Yes. You have to use another package HashLib4Pascal from same authors. I use that package for PKDF2.

  • Like 1

Share this post


Link to post
15 hours ago, stijnsanders said:

If you're interested in another alternative, I've started from the root document to make a pure-Delphi version under a permissive license: https://github.com/stijnsanders/tools/blob/master/crypto/aes.pas

I also did HMAC and PKDF2 here

Maybe it is worthwhile to move the 'crypto' library to a new, separate repository?

In a common, large repo it is difficult for anyone to find it

  • Like 2

Share this post


Link to post
23 hours ago, Kas Ob. said:

@stijnsanders That is beauty, one small step though is missing though, and it will not help the OP in his question, no IV or block modes, and i don't think Soji can add those on his own, the most beautiful thing in your library is the clean code and uses clause.

 

@Ondrej Kelle I know CryptoLib4Pascal for long time but never used it, it is great, now i was browsing the code one thing caught my eye, i think the PKCS7 padding is wrong, as it does accept 0 byte padding and it should not, in other words you should never end up with a byte value 0 at the end, if the length of data is multiple of k then you go and add full block, i think the relation between these should be redesigned:

TPaddedBufferedBlockCipher.DoFinal  // does call AddPadding without handling the return value that is not a big deal, but FbufOff should be [1..k] and should never be 0, from https://tools.ietf.org/html/rfc5652#section-6.3

TPkcs7Padding.AddPadding // don't check if inOff is 0

 

Also i tried to run the tests and play with it, but that package is missing a file HlpIhash, either that or i am missing something.

I hope @Ugochukwu Mmaduekwe can make some comments about PKCS7 padding here.

 

I have all below repositories and do not have problem with compilation etc. HlpIhash is part of HashLib4Pascal

https://github.com/Xor-el/CryptoLib4Pascal

https://github.com/Xor-el/HashLib4Pascal

https://github.com/Xor-el/SimpleBaseLib4Pascal

 

Edited by ertank

Share this post


Link to post
On 10/21/2020 at 7:13 PM, Kas Ob. said:

DEC library will not work, as that script uses Rfc2898DeriveBytes https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes?view=netcore-3.1

So you need PBKDF2 with HMACSHA1, both i believe supported and available in mOrMot library as Kryvich suggested.

Not sure, but the hash classes support KDF2. After fixing bugs even KDF1 and 3.

Share this post


Link to post
9 hours ago, TurboMagic said:

Not sure, but the hash classes support KDF2. After fixing bugs even KDF1 and 3.

KDF2 implementation might be right, but i don't see the HMACSHA1 implementation, according to "Rfc2898DeriveBytes Class" https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes?view=net-5.0

Quote

Implements password-based key derivation functionality, PBKDF2, by using a pseudo-random number generator based on HMACSHA1.

 

Does DEC library support HMAC ? i might be missing something but i don't see HMAC implementation in the library.

 

https://en.wikipedia.org/wiki/HMAC does have the implementation explained with one test vector for HMACSHA1.

Share this post


Link to post
On 1/18/2021 at 11:33 PM, TurboMagic said:

Not sure, but the hash classes support KDF2. After fixing bugs even KDF1 and 3.

Still lingering in my head and i think you missed the fact that KDFx and PBKDFx are different beasts.

 

I never tried DEC library so here is my first try to implement HMAC with DEC, i might be wrong with the usage of library itself but not with the implementation or the result, if you already didn't implement it yet then please use this this as you see fit

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  DECFormat,
  DECHash,
  DECHashBase,
  DECFormatBase;

function Calc_HMAC(const Key, Text: TBytes; HashClass: TDECHashClass): TBytes;
var
  Hash: TDECHash;
  TrimmedKey, PaddedKey, InnerKeyPad, OuterKeyPad: TBytes;
  I, BlockSize: Integer;
begin
  Hash := HashClass.Create;
  try
    BlockSize := Hash.BlockSize; // 64 for sha1, ...
    if Length(Key) > BlockSize then
      TrimmedKey := Hash.CalcBytes(Key)
    else
    begin
      SetLength(TrimmedKey, Length(Key));
      Move(Key[0], TrimmedKey[0], Length(Key));
    end;

    SetLength(PaddedKey, BlockSize);
    FillChar(PaddedKey[0], BlockSize, 0);
    {if Length(TrimmedKey) < BlockSize then
      FillChar(PaddedKey[Length(TrimmedKey)], BlockSize - Length(TrimmedKey), 0);}
    Move(TrimmedKey[0], PaddedKey[0], Length(TrimmedKey));

    SetLength(InnerKeyPad, BlockSize);
    SetLength(OuterKeyPad, BlockSize);
    for I := 0 to BlockSize - 1 do
    begin
      InnerKeyPad[I] := PaddedKey[I] xor $36;
      OuterKeyPad[I] := PaddedKey[I] xor $5C;
    end;

    Hash.Init;
    Hash.Calc(InnerKeyPad[0], BlockSize);
    Hash.Calc(Text[0], Length(Text));
    Hash.Done;
    PaddedKey := Hash.DigestAsBytes;

    Hash.Init;
    Hash.Calc(OuterKeyPad[0], BlockSize);
    Hash.Calc(PaddedKey[0], Length(PaddedKey));
    Hash.Done;
    Result := Hash.DigestAsBytes;

  finally
    Hash.Free;
  end;
end;

procedure TestHMac;
var
  Res: TBytes;
  AAx80String: TBytes;
begin
  // test vectors from https://en.wikipedia.org/wiki/HMAC
  Res := Calc_HMAC(BytesOf('key'), BytesOf('The quick brown fox jumps over the lazy dog'), THash_MD5);
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(Res)));
  Res := Calc_HMAC(BytesOf('key'), BytesOf('The quick brown fox jumps over the lazy dog'), THash_SHA1);
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(Res)));
  Res := Calc_HMAC(BytesOf('key'), BytesOf('The quick brown fox jumps over the lazy dog'), THash_SHA256);
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(Res)));

  // long key/data test vectors from https://tools.ietf.org/html/rfc2202
  SetLength(AAx80String, 80);
  FillChar(AAx80String[0], 80, $AA);

  Res := Calc_HMAC(AAx80String, BytesOf('Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data'), THash_MD5);
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(Res)));
  Res := Calc_HMAC(AAx80String, BytesOf('Test Using Larger Than Block-Size Key - Hash Key First'), THash_MD5);
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(Res)));
  Res := Calc_HMAC(AAx80String, BytesOf('Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data'), THash_SHA1);
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(Res)));
  Res := Calc_HMAC(AAx80String, BytesOf('Test Using Larger Than Block-Size Key - Hash Key First'), THash_SHA1);
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(Res)));
end;

begin
  TestHMac;
  Readln;
end.

and the results

Quote

80070713463E7749B90C2DC24911E275
DE7C9B85B8B78AA6BC8A7A36F70A90701C9DB4D9
F7BC83F430538424B13298E6AA6FB143EF4D59A14946175997479DBC2D1A3CD8
6F630FAD67CDA0EE1FB1F562DB3AA53E
6B1AB7FE4BD7BF8F0B62E6CE61B9D0CD
E8E99D0F45237D786D6BBAA7965C7808BBFF1A91
AA4AE5E15272D00E95705637CE8A3B55ED402112

 

That was simple implementation and it will suffer from slowness due the excess use of TBytes, many things can and should be enhanced but will affect the readability and security, for readability it should not be a problem after been tested for correctness and integrity, for integrity (and corner cases) i will try to adjust few things and repost here, and please if you are not interested then tell me to not waste my time.

Share this post


Link to post

No answer !

I am still in the mood to dig this, so here is an optimized version that handle 0 lengths with irritate the compiler when overflow checking is enabled

function Calc_HMAC(const Key, Text: TBytes; HashClass: TDECHashClass): TBytes;
const
  CONST_UINT_OF_0x36 = $3636363636363636;
  CONST_UINT_OF_0x5C = $5C5C5C5C5C5C5C5C;
var
  Hash: TDECHash;
  InnerKeyPad, OuterKeyPad: array[0..127] of Byte;    // 128 will fit all, but it should be based on HashClass.BlockSize
  I, KeyLength, BlockSize, DigestLength: Integer;
begin
  Hash := HashClass.Create;
  try
    BlockSize := Hash.BlockSize; // 64 for sha1, ...
    DigestLength := Hash.DigestSize;
    KeyLength := Length(Key);
    I := 0;
    if KeyLength > BlockSize then
    begin
      Result := Hash.CalcBytes(Key);
      KeyLength := DigestLength;
    end
    else
      Result := Key;

    while I <= KeyLength - SizeOf(NativeUInt) do
    begin
      PNativeUInt(@InnerKeyPad[I])^ := PNativeUInt(@Result[I])^ xor CONST_UINT_OF_0x36;
      PNativeUInt(@OuterKeyPad[I])^ := PNativeUInt(@Result[I])^ xor CONST_UINT_OF_0x5C;
      Inc(I, SizeOf(NativeUInt));
    end;
    while I < KeyLength do
    begin
      InnerKeyPad[I] := Result[I] xor $36;
      OuterKeyPad[I] := Result[I] xor $5C;
      Inc(I);
    end;

    while I <= BlockSize - SizeOf(NativeUInt) do
    begin
      PNativeUInt(@InnerKeyPad[I])^ := NativeUInt(CONST_UINT_OF_0x36);
      PNativeUInt(@OuterKeyPad[I])^ := NativeUInt(CONST_UINT_OF_0x5C);
      Inc(I, SizeOf(NativeUInt));
    end;
    while I < BlockSize do
    begin
      InnerKeyPad[I] := $36;
      OuterKeyPad[I] := $5C;
      Inc(I);
    end;

    Hash.Init;
    Hash.Calc(InnerKeyPad[0], BlockSize);
    if Length(Text) > 0 then
      Hash.Calc(Text[0], Length(Text));
    Hash.Done;
    Result := Hash.DigestAsBytes;

    Hash.Init;
    Hash.Calc(OuterKeyPad[0], BlockSize);
    Hash.Calc(Result[0], DigestLength);
    Hash.Done;
    Result := Hash.DigestAsBytes;

  finally
    Hash.Free;
  end;
end;

Few things and notes

1) I added length=0 checking and these can be removed or changed to conditional compiling

2) Removed all managed types (TBytes) this will increase speed 

3) Xoring and filling should be faster this way, as we have relatively short lengths so FillChar can be skipped

4) the length of the local PaddedKey are fixed to 128 bytes, 128 should fit all available algorithms with DEC library, should be adjusted when in the future new algorithm with longer BlockSize, also can be declared as constant somewhere.

5) The HMAC calculation algorithm is the core of PBKDF, so speed is very important here, and separated implementation of the above HMAC per algorithm would be even better like HMACSHA1, HMACSHA256... , also implementing the popular PBKDF2 without using the above method (separated algorithm) like infusing HMAC with PBKDF in code will remove the Hahs.Create..Free and will allow to reuse the padded key buffers hence will yield better performance.

 

ps: though the results is right, still not sure if i am using the Hash.Init,Calc,Done in the best way, this i think for you to see and correct.

  • Thanks 1

Share this post


Link to post

Hello,
 

thanks for this implementation and your effort doing it!
I have saved the stuff locally so I will include, this as soon as time permits, into the development branch in some form.
I need to look at this HMAC stuff myself first (today I've got lack of time) and then decide where to put it.

Your test will have to be changed into a DUnit test.

And I would put your name Kas Ob. into the list of contributors contained in the project if you don't object.

 

Thanks

TurboMagic

  • Like 1

Share this post


Link to post
2 minutes ago, TurboMagic said:

Your test will have to be changed into a DUnit test.

And I would put your name Kas Ob. into the list of contributors contained in the project if you don't object.

The tests are just samples, and for the name mentioning, it doesn't worth it, as i think you might want to implement class for it or something.

Share this post


Link to post
On 1/20/2021 at 5:36 PM, Kas Ob. said:

The tests are just samples, and for the name mentioning, it doesn't worth it, as i think you might want to implement class for it or something.

I just implemented your HMAC code. It is in the development branch now. Unit tests still need to follow.

If you look at my solution you can see how I was able to spare the hash class parameter...

  • Like 1

Share this post


Link to post
19 hours ago, Ian Branch said:

FWIW see attached.

Regrettably, I can't recall where I downloaded it from.

EIAES Encryption.zip

Thanks for sharing this, but I don't see what this provides compared to what the other mentioned solutions here already provide.

Share this post


Link to post
8 minutes ago, TurboMagic said:

I just implemented your HMAC code. It is in the development branch now. Unit tests still need to follow.

If you look at my solution you can see how I was able to spare the hash class parameter...

Nice.

 

One thing though, you can use the first version ( supposed to be little slower ) for readability, it is just cleaner and easier to read, also the KeyPadding vars are dynamic based on the BlockSize, but it does need the checks for 0 length, as for the speed it will matter only with PBKDF2, about that, i will provide you with working PBKDF2,  if not tomorrow morning then by the weekend i think will find the mood to prepare it for you. 

Share this post


Link to post
1 hour ago, TurboMagic said:

I just implemented your HMAC code. It is in the development branch now. Unit tests still need to follow.

If you look at my solution you can see how I was able to spare the hash class parameter...

I tried to do this and hope you like it, and of course please test it (in case i missed something) and use it as you see fit.

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  DECFormat,
  DECHash,
  DECHashBase,
  DECFormatBase,
  Windows;

function PBKDF2(const Password, Salt: TBytes; Iterations: Integer; KeyLength: Integer; HashClass: TDECHashClass): TBytes;
const
  CONST_UINT_OF_0x36 = $3636363636363636;
  CONST_UINT_OF_0x5C = $5C5C5C5C5C5C5C5C;
var
  Hash: TDECHash;
  I, J, C: Integer;
  BlockCount, HashLengthRounded, SaltLength: Integer;
  PassLength, DigestLength, BlockSize: Integer;
  InnerKeyPad, OuterKeyPad: TBytes;
  SaltEx, T, U, TrimmedKey: TBytes;
begin
  Hash := HashClass.Create;
  try
    // Setup needed parameters
    DigestLength := Hash.DigestSize;
    HashLengthRounded := DigestLength - SizeOf(NativeUInt) + 1;
    BlockCount := Trunc((KeyLength + DigestLength - 1) / DigestLength);
    BlockSize := Hash.BlockSize;
    PassLength := Length(Password);
    SaltLength := Length(Salt);
    SaltEx := Salt;
    SetLength(SaltEx, SaltLength + 4);  // reserve 4 bytes for INT_32_BE(i)
    SetLength(T, DigestLength);

    // Prepare Key for HMAC calculation
    // PrepareKeyForHMAC;
    I := 0;
    if PassLength > BlockSize then
    begin
      TrimmedKey := Hash.CalcBytes(Password);
      PassLength := DigestLength;
    end
    else
      TrimmedKey := Password;

    SetLength(InnerKeyPad, BlockSize);
    SetLength(OuterKeyPad, BlockSize);
    while I < PassLength do
    begin
      InnerKeyPad[I] := TrimmedKey[I] xor $36;
      OuterKeyPad[I] := TrimmedKey[I] xor $5C;
      Inc(I);
    end;
    while I < BlockSize do
    begin
      InnerKeyPad[I] := $36;
      OuterKeyPad[I] := $5C;
      Inc(I);
    end;

    // Calculate DK
    for I := 1 to BlockCount do
    begin
      SaltEx[SaltLength + 0] := Byte(I shr 24);   // INT_32_BE(i)
      SaltEx[SaltLength + 1] := Byte(I shr 16);
      SaltEx[SaltLength + 2] := Byte(I shr 8);
      SaltEx[SaltLength + 3] := Byte(I shr 0);
      FillChar(T[0], DigestLength, 0);            // reset Ti / F
      U := SaltEx;                                // initialize U to U1 = Salt + INT_32_BE(i)
      // Calculate F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc
      for C := 1 to Iterations do
      begin
        Hash.Init;
        Hash.Calc(InnerKeyPad[0], BlockSize);
        Hash.Calc(U[0], Length(U));
        Hash.Done;
        U := Hash.DigestAsBytes;

        Hash.Init;
        Hash.Calc(OuterKeyPad[0], BlockSize);
        Hash.Calc(U[0], DigestLength);
        Hash.Done;
        U := Hash.DigestAsBytes;                  // Ui
        // F = U1 ^ U2 ^ ... ^ Uc
        J := 0;
        while J < HashLengthRounded do
        begin
          PNativeUInt(@T[J])^ := PNativeUInt(@T[J])^ xor PNativeUInt(@U[J])^;
          Inc(J, SizeOf(NativeUInt));
        end;
        while J < DigestLength do
        begin
          T[J] := T[J] xor U[J];
          Inc(J);
        end;
      end;

      Result := Result + T;                       // DK += F    , DK = DK || Ti
    end;
  finally
    Hash.Free;
  end;

  // Trim to the needed key length
  SetLength(Result, KeyLength);
end;

procedure TestPBKDF2;
var
  S, E: Int64;
begin
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(PBKDF2(BytesOf('password'), BytesOf('salt'), 1, 20, THash_SHA1))));
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(PBKDF2(BytesOf('password'), BytesOf('salt'), 1, 32, THash_SHA256))));
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(PBKDF2(BytesOf('password'), BytesOf('salt'), 2, 32, THash_SHA256))));
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(PBKDF2(BytesOf('password'), BytesOf('salt'), 4096, 32, THash_SHA256))));

  // PBKDF2-HMAC-SHA512  test vectors from https://stackoverflow.com/questions/15593184/pbkdf2-hmac-sha-512-test-vectors
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(PBKDF2(BytesOf('password'), BytesOf('salt'), 1, 64, THash_SHA512))));
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(PBKDF2(BytesOf('password'), BytesOf('salt'), 2, 64, THash_SHA512))));
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(PBKDF2(BytesOf('password'), BytesOf('salt'), 4096, 64, THash_SHA512))));

  QueryPerformanceCounter(S);
  Writeln(StringOf(ValidFormat(TFormat_HEX).Encode(PBKDF2(BytesOf('password'), BytesOf('salt'), 1000000, 32, THash_SHA256))));
  QueryPerformanceCounter(E);
  Writeln(E - S);
end;

begin
  TestPBKDF2;
  Readln;
end.

The output

Quote

0C60C80F961F0E71F3A9B524AF6012062FE037A6
120FB6CFFCF8B32C43E7225256C4F837A86548C92CCC35480805987CB70BE17B
AE4D0C95AF6B46D32D0ADFF928F06DD02A303F8EF3C251DFD6E2D85A95474C43
C5E478D59288C841AA530DB6845C4C8D962893A001CE4E11A4963873AA98134A
867F70CF1ADE02CFF3752599A3A53DC4AF34C7A669815AE5D513554E1C8CF252C02D470A285A0501BAD999BFE943C08F050235D7D68B1DA55E63F73B60A57FCE
E1D9C16AA681708A45F5C7C4E215CEB66E011A2E9F0040713F18AEFDB866D53CF76CAB2868A39B9F7840EDCE4FEF5A82BE67335C77A6068E04112754F27CCF4E
D197B1B33DB0143E018B12F3D1D1479E6CDEBDCC97C5C0F87F6902E072F457B5143F30602641B3D55CD335988CB36B84376060ECD532E039B742A239434AF2D5
505112A590BE61AC9D3A235BF0A8EECEA40E54652EC0E3C257C227C9AA5E664C
5837322

 

Share this post


Link to post

I implemented first unit tests for the HMAC implementation you contributed the basis for now and set the array length of the two internal arrays dynamically now.

It's in the development branch.

 

Further unit tests and implementation of your other code donation later.

Share this post


Link to post

I get a crash when implementing HMAC unit tests for your other test vectors!

When performing the first test (using MD5) for long key/data test vectors from https://tools.ietf.org/html/rfc2202

I get a crash ERangeError on the first assignment in this part of the HMAC method:

 

    while I <= KeyLength - SizeOf(NativeUInt) do
    begin
      PNativeUInt(@InnerKeyPad[I])^ := PNativeUInt(@Result[I])^ xor CONST_UINT_OF_0x36;
      PNativeUInt(@OuterKeyPad[I])^ := PNativeUInt(@Result[I])^ xor CONST_UINT_OF_0x5C;
      Inc(I, SizeOf(NativeUInt));
    end;

The right hand side of the first assignment results, according to the debugger in this: 3906369333279686841

Size of native UInt in Win32 is 4 byte thus maximum number is this: 4294967296.

 

Turning off range/index/ E/A compiler checking would fix this, but is this a good idea?
If so I'd do this locally in the method.

 

What's your opinion?

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×