Soji 1 Posted October 21, 2020 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
Kryvich 165 Posted October 21, 2020 mORMot has AES encryption/decryption. 1 Share this post Link to post
Guest Posted October 21, 2020 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. Share this post Link to post
stijnsanders 35 Posted October 21, 2020 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 3 4 Share this post Link to post
Wil van Antwerpen 25 Posted October 21, 2020 (edited) Another solution is to use LockBox / LockBox3 Note that you can install LockBox via the GetIt manager in the delphi Studio. Edited October 21, 2020 by Wil van Antwerpen 1 Share this post Link to post
Guest Posted October 22, 2020 @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
Soji 1 Posted October 22, 2020 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. Share this post Link to post
Jacek Laskowski 57 Posted October 22, 2020 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 1 Share this post Link to post
ertank 27 Posted October 23, 2020 (edited) 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 October 23, 2020 by ertank Share this post Link to post
Soji 1 Posted October 23, 2020 Thanks for all the inputs. CryptoLib4Pascal worked for me. Share this post Link to post
TurboMagic 92 Posted January 18, 2021 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
Guest Posted January 19, 2021 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
Guest Posted January 20, 2021 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
Guest Posted January 20, 2021 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. Share this post Link to post
TurboMagic 92 Posted January 20, 2021 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 1 Share this post Link to post
Guest Posted January 20, 2021 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
Ian Branch 127 Posted January 20, 2021 FWIW see attached. Regrettably, I can't recall where I downloaded it from. EIAES Encryption.zip 1 Share this post Link to post
TurboMagic 92 Posted January 21, 2021 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... Share this post Link to post
TurboMagic 92 Posted January 21, 2021 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
Guest Posted January 21, 2021 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
Guest Posted January 21, 2021 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
TurboMagic 92 Posted January 23, 2021 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
TurboMagic 92 Posted January 23, 2021 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