#1 2016-07-25 11:34:47

jaclas
Member
Registered: 2014-09-12
Posts: 215

SynCrypto - strange exception in decrypt AES

Please see example code (simple console project, please copy and save as Project2.dpr and run):

program Project2;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Classes,
  SynCrypto;

function convertSB (const arg: AnsiString): TBytes;
var
   i: integer;
begin
  Setlength(Result, Length(arg));
   for i := 0 to length(arg) - 1 do
   begin
      result[i] := ord (arg [i + 1]);
   end;
end;

function EASstreamCrypt(aEASClass: TAESAbstractClass; const aInStream, aOutStream: TStream; const aPassword: TBytes): Integer;
var
 aKeySize: Integer;
 lAES : TAESAbstract;
 lBytesIn : TBytes;
 lBytesOut : TBytes;
begin
 aKeySize := Length(aPassword) shl 3;
 Assert((aKeySize = 128) or (aKeySize = 192) or (aKeySize = 256));
 lAES := aEASClass.Create(aPassword, aKeySize);
 try
 SetLength(lBytesIn, aInStream.Size);
 aInStream.Position := 0;
 aInStream.ReadBuffer(lBytesIn[0], aInStream.Size);
 lBytesOut := lAES.EncryptPKCS7(lBytesIn, True);
 Result := Length(lBytesOut);
 aOutStream.Write(lBytesOut[0], Length(lBytesOut));
 finally
 lAES.Free;
 end;
end;

function EASstreamDecrypt(aEASClass: TAESAbstractClass; aInStream, aOutStream: TStream; const aPassword: TBytes):
    Integer;
var
 aKeySize: Integer;
 lAES : TAESAbstract;
 lBytesIn : TBytes;
 lBytesOut : TBytes;
begin
 aKeySize := Length(aPassword) shl 3;
 Assert((aKeySize = 128) or (aKeySize = 192) or (aKeySize = 256));
 lAES := aEASClass.Create(aPassword, aKeySize);
 try
 SetLength(lBytesIn, aInStream.Size);
 aInStream.Position := 0;
 aInStream.ReadBuffer(lBytesIn[0], aInStream.Size);
 lBytesOut := lAES.DecryptPKCS7(lBytesIn, True);
 Result := Length(lBytesOut);
 aOutStream.Write(lBytesOut[0], Length(lBytesOut));
 finally
 lAES.Free;
 end;
end;

procedure streamCryptWrapper(const aInStream, aOutStream: TStream; const aPassword: TBytes);
begin
  EASstreamCrypt(TAESCBC, aInStream, aOutStream, aPassword);
end;

procedure streamDecryptWrapper(const aInStream, aOutStream: TStream; const aPassword: TBytes);
begin
   EASstreamDecrypt(TAESCBC, aInStream, aOutStream, aPassword);
end;

procedure SelfTest;
var
 decodedString: String;
 inputString: string;
 InStr, OutStr : TMemoryStream;
 InputStringStream : TStringStream;
 lInStrStream, lOutStrStream : TStringStream;
 lPasswordBytes: TBytes;
begin
  inputString := 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque hendrerit augue quis lacus efficitur,' +
          ' sollicitudin tempor sapien dapibus. Vivamus quis neque congue, imperdiet nisi nec, hendrerit nulla.' +
          ' Ut quis dolor eget sapien sodales faucibus vel at turpis. Aenean sed varius arcu.';
  lPasswordBytes := convertSB('01234567890123456789012345678901');
  InStr := TMemoryStream.Create;
  OutStr := TMemoryStream.Create;
  InputStringStream := TStringStream.Create(inputString, TEncoding.Unicode);
  try

  // -------------------------------------------------------------------------
  // TEST 1
  InStr.Clear;
  OutStr.Clear;
  InStr.CopyFrom(InputStringStream, 0);
  InStr.Position := 0;
  // crypt
  EASstreamCrypt(TAESCBC, InStr, OutStr, lPasswordBytes);
  //decrypt
  InStr.Clear;
  EASstreamDecrypt(TAESCBC, OutStr, InStr, lPasswordBytes);
  decodedString := 'empty';
  decodedString := Copy(PChar(InStr.Memory), 1, Length(inputString));
  Assert(decodedString = inputString);
  // TEST 1 PASSED

  // -------------------------------------------------------------------------
  // TEST 2 with simple wrapper
  InStr.Clear;
  OutStr.Clear;
  InStr.CopyFrom(InputStringStream, 0);
  InStr.Position := 0;
  // crypt
  streamCryptWrapper(InStr, OutStr, lPasswordBytes);

  InStr.Clear;
  // decrypt
  streamDecryptWrapper(OutStr, InStr, lPasswordBytes);  //   <--- ESynCrypto exception: TAESCBC.DecryptPKCS7: Invalid content
  decodedString := 'pustka';
  InStr.Position := 0;
  decodedString := Copy(PChar(InStr.Memory), 1, Length(inputString));
  Assert(decodedString = inputString);
  finally
  InStr.Free;
  OutStr.Free;
  InputStringStream.Free;
  end;
end;


begin
  try
   SelfTest;
  except
    on E: ESynCrypto do
      Writeln('ESynCrypto: ', E.Message);
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

This line raise exception:

  streamDecryptWrapper(OutStr, InStr, lPasswordBytes);  //   <--- ESynCrypto exception: TAESCBC.DecryptPKCS7: Invalid content

The code performs two tests, the first test is done by directly calling functions:

EASstreamCrypt / EASstreamDecrypt

a second test calls a very simple wrappers:

streamCryptWrapper / streamDecryptWrapper

procedure streamCryptWrapper(const aInStream, aOutStream: TStream; const aPassword: TBytes);
begin
  EASstreamCrypt(TAESCBC, aInStream, aOutStream, aPassword);
end;

...and I get exception, but I don't understand why sad

I was looking for a problem a few hours, please help me. Thanks.

Last edited by jaclas (2016-08-05 11:20:00)

Offline

#2 2016-07-29 15:26:43

jaclas
Member
Registered: 2014-09-12
Posts: 215

Re: SynCrypto - strange exception in decrypt AES

And nothing? Nobody helps me out? Please check my code :-(

Offline

#3 2016-08-05 14:17:32

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,726
Website

Re: SynCrypto - strange exception in decrypt AES

Your code is just hard to read and debug.

Offline

#4 2016-08-05 22:22:11

jaclas
Member
Registered: 2014-09-12
Posts: 215

Re: SynCrypto - strange exception in decrypt AES

I add some comments:

program Project2;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Classes,
  SynCrypto;

//helper function to convert ansi string to TBytes
function convertSB (const arg: AnsiString): TBytes;
var
   i: integer;
begin
  Setlength(Result, Length(arg));
   for i := 0 to length(arg) - 1 do
   begin
      result[i] := ord (arg [i + 1]);
   end;
end;

//function to encrypt aInStream used aPassword and aAESClass, result is saved to aOutStream
function AESstreamCrypt(aAESClass: TAESAbstractClass; const aInStream, aOutStream: TStream; const aPassword: TBytes): Integer;
var
 aKeySize: Integer;
 lAES : TAESAbstract;
 lBytesIn : TBytes;
 lBytesOut : TBytes;
begin
 //get AES key size
 aKeySize := Length(aPassword) shl 3;

 //create an AES class object to encrypt stream
 lAES := aAESClass.Create(aPassword, aKeySize);
 try

 //copy aInStream to TBytes local variable
 SetLength(lBytesIn, aInStream.Size);
 aInStream.Position := 0;
 aInStream.ReadBuffer(lBytesIn[0], aInStream.Size);

 //encrypt TBytes
 lBytesOut := lAES.EncryptPKCS7(lBytesIn, True);
 Result := Length(lBytesOut);

 //copy encrypted TBytes to aOutStream
 aOutStream.Write(lBytesOut[0], Length(lBytesOut));
 finally
 lAES.Free;
 end;
end;

//function to decrypt aInStream used aPassword and aEASClass, result is saved to aOutStream
function AESstreamDecrypt(aAESClass: TAESAbstractClass; aInStream, aOutStream: TStream; const aPassword: TBytes):
    Integer;
var
 aKeySize: Integer;
 lAES : TAESAbstract;
 lBytesIn : TBytes;
 lBytesOut : TBytes;
begin
 //get AES key size
 aKeySize := Length(aPassword) shl 3;

 //create AES class object to decrypt stream
 lAES := aAESClass.Create(aPassword, aKeySize);
 try
 //copy aInStream to TBytes local variable
 SetLength(lBytesIn, aInStream.Size);
 aInStream.Position := 0;
 aInStream.ReadBuffer(lBytesIn[0], aInStream.Size);

 //decrypt TBytes
 lBytesOut := lAES.DecryptPKCS7(lBytesIn, True);
 Result := Length(lBytesOut);

 //copy decrypted TBytes to aOutStream
 aOutStream.Write(lBytesOut[0], Length(lBytesOut));
 finally
 lAES.Free;
 end;
end;

//simple wrapper for AESstreamCrypt function
procedure streamCryptWrapper(const aInStream, aOutStream: TStream; const aPassword: TBytes);
begin
  AESstreamCrypt(TAESCBC, aInStream, aOutStream, aPassword);
end;

//simple wrapper for AESstreamDecrypt function
procedure streamDecryptWrapper(const aInStream, aOutStream: TStream; const aPassword: TBytes);
begin
  AESstreamDecrypt(TAESCBC, aInStream, aOutStream, aPassword);
end;

//test direct crypt and wrapped crypt
procedure SelfTest;
var
 decodedString: String;
 inputString: string;
 InStr, OutStr : TMemoryStream;
 InputStringStream : TStringStream;
 lInStrStream, lOutStrStream : TStringStream;
 lPasswordBytes: TBytes;
begin
  inputString := 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque hendrerit augue quis lacus efficitur,' +
          ' sollicitudin tempor sapien dapibus. Vivamus quis neque congue, imperdiet nisi nec, hendrerit nulla.' +
          ' Ut quis dolor eget sapien sodales faucibus vel at turpis. Aenean sed varius arcu.';
  lPasswordBytes := convertSB('01234567890123456789012345678901');
  InStr := TMemoryStream.Create;
  OutStr := TMemoryStream.Create;
  InputStringStream := TStringStream.Create(inputString, TEncoding.Unicode);
  try

  // -------------------------------------------------------------------------
  // TEST 1 - use direct AESstreamCrypt and AESstreamDecrypt functions
  InStr.Clear;
  OutStr.Clear;
  InStr.CopyFrom(InputStringStream, 0);
  InStr.Position := 0;
  // crypt
  AESstreamCrypt(TAESCBC, InStr, OutStr, lPasswordBytes);
  //decrypt
  InStr.Clear;
  AESstreamDecrypt(TAESCBC, OutStr, InStr, lPasswordBytes);
  decodedString := 'empty';
  decodedString := Copy(PChar(InStr.Memory), 1, Length(inputString));
  Assert(decodedString = inputString);
  // TEST 1 PASSED

  // -------------------------------------------------------------------------
  // TEST 2 with wrappers streamCryptWrapper and streamDecryptWrapper
  InStr.Clear;
  OutStr.Clear;
  InStr.CopyFrom(InputStringStream, 0);
  InStr.Position := 0;
  // crypt
  streamCryptWrapper(InStr, OutStr, lPasswordBytes);

  InStr.Clear;
  // decrypt
  streamDecryptWrapper(OutStr, InStr, lPasswordBytes);  //   <--- ESynCrypto exception: TAESCBC.DecryptPKCS7: Invalid content
  decodedString := 'pustka';
  InStr.Position := 0;
  decodedString := Copy(PChar(InStr.Memory), 1, Length(inputString));
  Assert(decodedString = inputString);
  finally
  InStr.Free;
  OutStr.Free;
  InputStringStream.Free;
  end;
end;


begin
  try
   SelfTest;
  except
    on E: ESynCrypto do
      Writeln('ESynCrypto: ', E.Message);
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Last edited by jaclas (2016-08-05 22:24:37)

Offline

#5 2016-08-11 11:35:03

jaclas
Member
Registered: 2014-09-12
Posts: 215

Re: SynCrypto - strange exception in decrypt AES

FixInsight show warnings on SynCrypto, maybe unimportant, but...

[FixInsight Warning] SynCrypto.pas(6734): W517 Variable 'iv' hides a class field, method or property
[FixInsight Warning] SynCrypto.pas(6770): W517 Variable 'iv' hides a class field, method or property
[FixInsight Warning] SynCrypto.pas(6784): W517 Variable 'iv' hides a class field, method or property
[FixInsight Warning] SynCrypto.pas(5062): W521 Return value of function 'TAESFull.EncodeDecode' might be undefined

Offline

#6 2016-08-11 11:53:15

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,726
Website

Re: SynCrypto - strange exception in decrypt AES

Included fixes for FixInsight.
See http://synopse.info/fossil/info/38757df690
Those were not armful at all, but better to get rid of them.

Offline

#7 2016-08-11 12:10:00

jaclas
Member
Registered: 2014-09-12
Posts: 215

Re: SynCrypto - strange exception in decrypt AES

And again, I refactored and simplified example code, please look...

program Project2;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Classes,
  SynCrypto;

const
 //string to encrypting
 CInputString : string = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque hendrerit augue quis lacus efficitur,' +
          ' sollicitudin tempor sapien dapibus. Vivamus quis neque congue, imperdiet nisi nec, hendrerit nulla.' +
          ' Ut quis dolor eget sapien sodales faucibus vel at turpis. Aenean sed varius arcu.';
 //password
 CPassword : TBytes = [0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1];

 //AES initialization vector
 CIV: TAESBlock = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);


//function to encrypt aInStream used aPassword and aAESClass, result is saved to aOutStream
procedure AESstreamCrypt(aAESClass: TAESAbstractClass; const aInStream, aOutStream: TStream; const aPassword: TBytes);
var
 aKeySize: Integer;
 lAES : TAESAbstract;
 lIn : RawByteString;
 lOut : RawByteString;
begin
 //get AES key size
 aKeySize := Length(aPassword) shl 3;

 //create an AES class object to encrypt stream
 lAES := aAESClass.Create(aPassword, aKeySize);
 try

 //copy aInStream to RawByteString local variable
 SetLength(lIn, aInStream.Size);
 aInStream.Position := 0;
 aInStream.ReadBuffer(lIn[1], aInStream.Size);

 //assign const vector
 lAES.IV := CIV;
 //encrypt TBytes
 lOut := lAES.EncryptPKCS7(lIn, False);

 //copy encrypted RawByteString to aOutStream
 aOutStream.Write(lOut[1], Length(lOut));
 finally
 lAES.Free;
 end;
end;

//function to decrypt aInStream used aPassword and aEASClass, result is saved to aOutStream
procedure AESstreamDecrypt(aAESClass: TAESAbstractClass; aInStream, aOutStream: TStream; const aPassword: TBytes);
var
 aKeySize: Integer;
 lAES : TAESAbstract;
 lIn : RawByteString;
 lOut: RawByteString;
begin
 //get AES key size
 aKeySize := Length(aPassword) shl 3;

 //create AES class object to decrypt stream
 lAES := aAESClass.Create(aPassword, aKeySize);
 try
 //copy aInStream to RawByteString local variable
 SetLength(lIn, aInStream.Size);

 aInStream.Position := 0;
 aInStream.ReadBuffer(lIn[1], aInStream.Size);

 //assign const vector
 lAES.IV := CIV;
 //decrypt TBytes
 lOut := lAES.DecryptPKCS7(lIn, False);

 //copy decrypted RawByteString to aOutStream
 aOutStream.Write(lOut[1], Length(lOut));
 finally
 lAES.Free;
 end;
end;

//simple wrapper on crypt method which only passes arguments to crypt method
procedure streamCryptWrapper(const aInStream, aOutStream: TStream; const aPassword: TBytes);
begin
  AESstreamCrypt(TAESCBC, aInStream, aOutStream, aPassword);
end;

//simple wrapper on decrypt method which only passes arguments to decrypt method
procedure streamDecryptWrapper(const aInStream, aOutStream: TStream; const aPassword: TBytes);
begin
  AESstreamDecrypt(TAESCBC, aInStream, aOutStream, aPassword);
end;

procedure SelfTest;
var
 decodedString: String;
 InStr, OutStr : TStringStream;
begin
  InStr := TStringStream.Create;
  OutStr := TStringStream.Create;
  try

  // TEST 1 ------------------------------------------------------------------
  InStr.Clear;
  OutStr.Clear;

  //put string into stream
  InStr.WriteString(CInputString);

  // crypt stream InStr -> OutStr
  AESstreamCrypt(TAESCBC, InStr, OutStr, CPassword);

  //decrypt stream OutStr -> InStr
  InStr.Clear;
  AESstreamDecrypt(TAESCBC, OutStr, InStr, CPassword);

  //check decoded string
  decodedString := InStr.DataString;
  Assert(decodedString = CInputString);
  // TEST 1 PASSED
  // end of TEST 1 -----------------------------------------------------------

  // -------------------------------------------------------------------------
  // TEST 2 with simple wrapper -  cause exception!
  InStr.Clear;
  OutStr.Clear;

  //put string into stream
  InStr.WriteString(CInputString);

  // crypt stream InStr -> OutStr
  streamCryptWrapper(InStr, OutStr, CPassword);

  // decrypt stream OutStr -> InStr
  InStr.Clear;
  streamDecryptWrapper(OutStr, InStr, CPassword); // <--- ESynCrypto exception: TAESCBC.DecryptPKCS7: Invalid content, why??

  //check decoded string
  decodedString := InStr.DataString;
  Assert(decodedString = CInputString);
  // end of TEST 2 -----------------------------------------------------------
  finally
  InStr.Free;
  OutStr.Free;
  end;
end;


begin
  try
   SelfTest;
  except
    on E: ESynCrypto do
      Writeln('ESynCrypto: ', E.Message); //TAESCBC.DecryptPKCS7: Invalid content !!!
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

...and thanks for time.


The main problem is a bug when I use wrappers, I don't know why :-(

Last edited by jaclas (2016-08-11 12:11:39)

Offline

#8 2016-08-11 12:30:01

jaclas
Member
Registered: 2014-09-12
Posts: 215

Re: SynCrypto - strange exception in decrypt AES

I found error!!! Of course in my code... :-(

Offline

Board footer

Powered by FluxBB