AES decryption fails with invalid key length

I’m also experiencing issues with encryption/decryption using TAESEncription – it no longer works for me.

During my research, I found this forum entry:
:backhand_index_pointing_right: Invalid Operation: -207 - #14 by Reimer_Bjorn

To investigate further, I tried running the demo project CryptoDemo (located in Products\tms.vcl.crypto\Demos\VCL).
Unfortunately, it does not run correctly either:

  • In the DPR file, FastMM4 is not found and must be removed:
program CryptoDemo;

uses
  FastMM4,
  Vcl.Forms,
  • In the unit uCryptoDemo, the unit ECIES is not found and must also be removed:
unit uCryptoDemo;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Diagnostics, System.DateUtils, System.UITypes, Vcl.FileCtrl,
  System.Classes, Vcl.Graphics, System.TimeSpan, System.IOUtils,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Imaging.pngimage, Vcl.ExtCtrls,
  Vcl.Buttons, Vcl.StdCtrls, Vcl.ComCtrls, AnsiStrings, Shellapi,
  CryptoConst, AESObj, RSAObj, ECCObj, Argon2d, HashObj, PBKDF2, SalsaObj,
  SPECKObj, MiscObj, X509Obj, XAdESObj, CAdESObj, PAdESObj, AdESObj, PKCS11Obj,
  TMSEncryptedIniFile, CryptBase, tlsh, Curves, ECIES;

After applying these corrections, the program compiles and runs.
On the AES tab, I entered a key and some text. Encryption seems to work, but during decryption the key length is flagged as invalid – even though the key is correct (see screenshot).


In my own program I get this exception:

Exception: Invalid Operation : -208

If I apply the suggested code changes from the forum entry above, I then get the error that the key length is invalid – again, even though the key is correct.
(The key is actually already in hex format, but the “-208” error message appears before conversion.)

Here’s my current decrypt function:

function Tdm.DecryptStr(AString: String): String;
begin
  try
    var Conv: TConvert;
    Conv := TConvert.Create(hexa);

    AES.AType        := atCBC;
    AES.KeyLength    := kl256;
    AES.OutputFormat := hexa;
    AES.Key          := Conv.FormatToChar(FKey);
    //AES.Key        := FKey;
    AES.PaddingMode  := TPaddingMode.PKCS7;
    AES.IVMode       := TIVMode.rand;
    AES.Unicode      := yesUni;

    Result := AES.Decrypt(Astring);
  except
    on E:Exception do
    begin
      ShowMessage(E.ClassName+': '+E.Message);
    end;
  end;
end;

:red_question_mark: How can this be fixed as simply as possible?

Thanks for spotting FastMM4 in the VCL demo, I forgot to remove it. ECIES was already gone (no longer needed in v5.x).

If the output of encryption is 'hexa', then setting inputFormat to hex should be enough as you IVmode is 'rand'. This will convert both key and Astring to 'raw' internally.

Will improve AES panel ASAP.

Hi,

We have the same problem with "Key length" = 256 bits and "Format" = Base64.

Error "Keylength incorrect, must be 32".

What can we do ? It's a major problem for us, all our licence system of our application is on failure with this error.

Thank you for your quick response.

Yes, I have seen this and fixed it this morning. I rewrote part of the AES demo and there are a few things to do if it urges.

In AESObj.pas, there is a bug in SetKey:

procedure TAESGCM.SetKey(const Value: string);
var
Conv: TConvert;

begin
Conv := TConvert.Create(inputFormat); <== inputFormat is missing, please, add it

Then in procedure TMainForm.Panel1Click(Sender: TObject);

AES.InputFormat := raw; // key, IV, text as 'raw'
AES.Key := Conv.FormatToChar(Edit1.Text);

Then, in the demo file, in procedure TMainForm.Panel2Click(Sender: TObject);

AES.InputFormat := AES.OutputFormat;

Then, in procedure TMainForm.Edit1Change(Sender: TObject);

Label4.Caption := '(' + IntToStr(Length(Edit1.Text)) + '/' + IntToStr(Conv.OutputFormatLength(kl)) + ')';

keyOK := Length(Edit1.Text) = Conv.OutputFormatLength(kl);

Then, in procedure TMainForm.Image5Click(Sender: TObject);

// convert to hexa to display printable characters
case ComboBox15.ItemIndex of
0: Conv.AType := hexa;
1: Conv.AType := base64;
2: Conv.AType := base64url;
else
Conv.AType := base32;
end;

s := Conv.RandomString(kl);

Finally, add an event in ComboBox15Change

procedure TMainForm.ComboBox15Change(Sender: TObject);
begin
Edit1.Color := clRed;
Edit1.Text := '';
end;

That's a lot of changes but it now works with all conversion formats.

Note that these changes should also be applied to AESMAC and AESGCM in the demo if you want to use formats other than 'hexa'.

In procedure TAESEncryption.SetKey(const Value: string);

if inputFormat <> raw then begin
Conv := TConvert.Create(inputFormat);

The procedure TAESGCM.SetKey looks to me like you described as the final solution:

procedure TAESGCM.SetKey(const Value: string);
var
  Conv: TConvert;

begin
  Conv := TConvert.Create(inputFormat);
  if Value <> '' then begin

The procedure TAESEncryption.SetKey is missing the InputFormat.

procedure TAESEncryption.SetKey(const Value: string);
var
  s: string;
  Conv: TConvert;

begin
  if inputFormat <> raw then begin
    Conv := TConvert.Create;

I have now only tested it with my code, but the error message about the key length still appears.

When I run my original code, I no longer get the ‘-208’ error message, however the key length is still being objected to.

Addition:
The ‘-208’ error has reappeared; it was probably just coincidence earlier.

function Tdm.DecryptStr(AString: String): String;
begin
  try
    var Conv: TConvert;
    Conv := TConvert.Create(hexa);

    AES.AType        := atCBC;
    AES.KeyLength    := kl256;
    AES.OutputFormat := hexa;
  //  AES.Key          := Conv.FormatToChar(FKey);
    AES.Key          := FKey;
    AES.PaddingMode  := TPaddingMode.PKCS7;
    AES.IVMode       := TIVMode.rand;
    AES.Unicode      := yesUni;

    Result := AES.Decrypt(Astring);
  except
    on E:Exception do
    begin
      Showmessage(E.ClassName+': '+E.Message);
    end;
  end;
end;

I think there are still other places that need to be adjusted.

Can you send me your encryption sequence at bernard@tmssoftware.com?

Ok, I’ve found the issue on my side.
It looks like in earlier versions the key length was shorter (kl256 -> 32 characters). At least in my case, the code was set up for that length and had been working fine for quite some time.

With the version change, this seems to have been modified. However, the new code still contains misleading error messages regarding this.

But step by step:

This is how the decryption looked before (simplified):

function Tdm.DecryptStr(AString: String): String;
begin
  AES.AType        := atCBC;
  AES.KeyLength    := kl256;
  AES.OutputFormat := hexa;
  AES.Key          := FKey;  // 32 characters
  AES.PaddingMode  := TPaddingMode.PKCS7;
  AES.IVMode       := TIVMode.rand;
  AES.Unicode      := yesUni;

  Result := AES.Decrypt(Astring);
end;

After switching to the new version, this code now throws the error message -208.
This happens because the key length of FKey is 32 characters.

If I increase the key length to 64 characters, I then get the message:
keylength incorrect, must be 32.

function Tdm.DecryptStr(AString: String): String;
begin
  AES.AType        := atCBC;
  AES.KeyLength    := kl256;
  AES.OutputFormat := hexa;
  AES.Key          := FKey64;
  AES.PaddingMode  := TPaddingMode.PKCS7;
  AES.IVMode       := TIVMode.rand;
  AES.Unicode      := yesUni;

  Result := AES.Decrypt(Astring);
end;

The order of the property definitions also seems to be critical.
As far as I remember, I originally copied them from the demo example.

In the attached demo project you’ll find the two error cases as well as a working test implementation.

During my research, I also noticed in AESCore.pas that nothing is coded in the constructor/destructor. Is this intentional?

Constructor TAESCore.Create;
begin

end;

Destructor TAESCore.Destroy;
begin

end;

AES_Test.zip (76.4 KB)

Good news!

Just on "in earlier versions the key length was shorter": no, the key was in 'raw' format by default, so the length was then 32 bytes for kl256. If you use a key converted to 'hexa', then the size is 64 bytes.
The new 'inputFormat' property allows users to select the format (raw, hexa, base64, base64url or base32). The 'issue' is that the format shall be applied to all parameters (key, IV, salt, etc.) and input (text string or file).

Then, you are correct that parameters for the key have an order. Because of the possible encoding of the key, it is not possible to identify the key size only with the number of bytes in the key. A key length is also required.

This could be solved using a combination of the format and the key, but will have to wait for the moment.

Hello,

Same here this code :

var
 Laes: TAESEncryption;
 LCipher,LResultat: string;
begin
    Laes:= TAESEncryption.Create;
    try
        Laes.AType:=              atCBC;
        Laes.KeyLength:=          kl256;
        Laes.Unicode :=           yesUni;
        Laes.Key:=                '12345678901234567890123456789012';
        Laes.OutputFormat:=       hexa;
        Laes.PaddingMode:=        TpaddingMode.PKCS7;
        Laes.IVMode:=             rand;
        // Crypte
        LCipher:= Laes.Encrypt('test');
        Showmessage(LCipher);
        // Uncrypt
        Laes.Decrypt( LCipher,LResultat); // same with Laes.Decrypt( LCipher);
        Showmessage(LResultat);

    finally
      Laes.Free;
    end;
end;

The line :

Laes.Decrypt( LCipher,LResultat); 

Raise random (99 % of time) ‘Invalid Operation ; -208’ error message on Delphi 11.3 and Delphi 12.3 (32 bits VCL). Version v5.0.9.5.Same code have no problem in version 4.X

And it's just a copy/paste from your "TMSCryptographyPack.pdf" !!!

That is indeed an inputFormat issue. You can fix it this way:

procedure TDemoForm.AESDebugBtnClick(Sender: TObject);
var
 Laes: TAESEncryption;
 LCipher,LResultat: string;
 Conv: TConvert;

begin
    Laes:= TAESEncryption.Create;
    Conv := TConvert.Create();
    try
        Laes.AType:=              atCBC;
        Laes.KeyLength:=          kl256;
        Laes.Unicode :=           yesUni;
        Laes.Key:=                '12345678901234567890123456789012'; // 'raw' key
        Laes.InputFormat :=       raw; // Does not exist in 4.3.3
        Laes.OutputFormat :=      hexa;
        Laes.PaddingMode:=        TpaddingMode.PKCS7;
        Laes.IVMode:=             rand; // will be converted to hexa in the cryptogram
        // Encrypt
        LCipher:= Laes.Encrypt('my little test'); // raw string, unicode
        MainMemo.Lines.Add('Cryptogram: ' + LCipher); // hex output
        // Decrypt
        // inputFormat is raw, convert Cryptogram that was in hex (you can also switch to 'hexa' and convert the key
        Conv.AType := hexa;
        LCipher := Conv.FormatToChar(LCipher);
        // we want the output as a raw string (or whatever else)
        Laes.OutputFormat := raw;
        Laes.Decrypt( LCipher,LResultat);
        MainMemo.Lines.Add('Cleartext: ' + LResultat);

    finally
      Conv.Free;
      Laes.Free;
    end;
end;

This approach works for me so far.
Thank you for providing the correct solution/workaround.
I really appreciate your support in resolving this. :slightly_smiling_face: