1

I am currently testing Lockbox3, and have run into a problem trying to do a simple string encryption & decryption using the XXTEA cipher. I'm using Delphi 10.1 Berlin and installed the latest version of Lockbox3 using GetIt! My test app is targeted for Win32 (VCL).

The issue is that the encryption works fine, but when I attempt to decrypt the string, an exception is thrown : "No mapping for the Unicode character exists in the target multi-byte code page.". I'm not sure what the issue is?

procedure TForm1.btnEncryptClick(Sender: TObject);
var
    PlainText : string;
   CipherText : string;
    CryptoLib : TCryptographicLibrary;
        Codec : TCodec;
begin
    try
      CryptoLib := TCryptographicLibrary.Create(nil);
      Codec := TCodec.Create(nil);
      Codec.CryptoLibrary := CryptoLib;
      Codec.StreamCipherId := 'native.XXTEA.Large.Littleend';
      Codec.ChainMode := 'native.CBC';
      Codec.Password := 'password';
      PlainText := Edit1.Text;
      Codec.EncryptString( PlainText, CipherText, Tencoding.UTF8 );
      Codec.Burn;
    finally
      Codec.Free;
      CryptoLib.Free;
      Edit2.Text := CipherText;
    end;
end;

procedure TForm1.btnDecryptClick(Sender: TObject);
var
     PlainText : string;
    CipherText : string;
     CryptoLib : TCryptographicLibrary;
         Codec : TCodec;
begin
    try
      CryptoLib := TCryptographicLibrary.Create(nil);
      Codec := TCodec.Create(nil);
      Codec.CryptoLibrary := CryptoLib;
      Codec.StreamCipherId := 'native.XXTEA.Large.Littleend';
      Codec.ChainMode := 'native.CBC';
      Codec.Password := 'password';
      CipherText := Edit2.Text;
      Codec.DecryptString( PlainText, CipherText, Tencoding.UTF8 );
      Codec.Burn;
    finally
      Codec.Free;
      CryptoLib.Free;
      Edit1.Text := PlainText;
    end;
end;
Adriaan Jutte
  • 53
  • 1
  • 3

1 Answers1

1

There are two issues. The first is that there is a defect in the XXTEA decryptor. If you can't wait for the fix to be published, I have posted a code delta that you can apply and recompile for an immediate solution. I am a bit surprised that there are no unit tests for XXTEA. Everything else in this library has plenty of unit tests, so I will work on that too.

The second issue is that the codec property names that you should set when doing it at runtime are slightly different from the design-time property names. (ChainModeId not ChainMode). The difference was made to make design-time property configuration easier.

Any way, I did some testing on Lockbox-3 v3.7.0 . The home site can be found at http://lockbox.seanbdurkin.id.au/HomePage and the source code from https://github.com/SeanBDurkin/tplockbox . I tested with Delphi 10.2 Tokyo; Target=Win32.

Listing one gives the test program (a one-file console program), and Listing two gives the fix. The fix is an update to method procedure TXXTEA_LargeBlock_LE_Decryptor.End_Decrypt() which can be found in unit TPLB3.XXTEA . The GetIt version will have slightly different unit names. That being said, in relation to XXTEA, it might be safer to wait until unit tests have been published.

Also, if any one wants to help out by contributing Known-Answer-Tests (KATs), that would be good for all.

Listing One: A Test program

program TestLockBox;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  TPLB3.CryptographicLibrary,
  TPLB3.Codec,
  TPLB3.Random,
  TPLB3.Constants;

var
  sMessage: string;
  sCipher: string;
  sRecon: string;

const
      CipherId    = XXTEA_Large_ProgId;  IsBlockMode = False;
  //  CipherId    = Twofish_ProgId;      IsBlockMode = True;
  ChainMode   = 'native.CBC';
  Password    = 'password';
  Seed        = 1;

function TForm1_btnEncryptClick( const sPlainText: string): string;
var
    PlainText : string;
   CipherText : string;
    CryptoLib : TCryptographicLibrary;
        Codec : TCodec;
begin
    try
      CryptoLib := TCryptographicLibrary.Create(nil);
      Codec := TCodec.Create(nil);
      Codec.CryptoLibrary := CryptoLib;
      if IsBlockMode then
          begin
          Codec.StreamCipherId := BlockCipher_ProgId;
          Codec.BlockCipherId  := CipherId
          end
        else
          Codec.StreamCipherId := CipherId;
      Codec.ChainModeId    := ChainMode;
      Codec.Password := Password;
      PlainText := sPlainText;
      Codec.EncryptString( PlainText, CipherText, Tencoding.UTF8 );
      Codec.Burn;
    finally
      Codec.Free;
      CryptoLib.Free;
      result := CipherText;
    end;
end;

function TForm1_btnDecryptClick( const sCipherText: string): string;
var
     PlainText : string;
    CipherText : string;
     CryptoLib : TCryptographicLibrary;
         Codec : TCodec;
begin
    try
      CryptoLib := TCryptographicLibrary.Create(nil);
      Codec := TCodec.Create(nil);
      Codec.CryptoLibrary := CryptoLib;
      if IsBlockMode then
          begin
          Codec.StreamCipherId := BlockCipher_ProgId;
          Codec.BlockCipherId  := CipherId
          end
        else
          Codec.StreamCipherId := CipherId;
      Codec.ChainModeId    := ChainMode;
      Codec.Password := Password;
      CipherText := sCipherText;
      Codec.DecryptString( PlainText, CipherText, Tencoding.UTF8 );
      Codec.Burn;
    finally
      Codec.Free;
      CryptoLib.Free;
      result := PlainText;
    end;
end;

begin
  if Seed = -1 then
      TRandomStream.Instance.Randomize
    else
      TRandomStream.Instance.Seed := Seed;
  try
    sMessage := 'Your lips are smoother than vasoline.';
    WriteLn( 'Plaintext="' + sMessage + '"');
    sCipher  := TForm1_btnEncryptClick( sMessage);
    WriteLn( 'Base64 representation of ciphertext=' + sCipher);
    sRecon := TForm1_btnDecryptClick( sCipher);
    WriteLn( 'The reconstructed plaintext = "' + sRecon + '"');
    if sMessage = sRecon then
        WriteLn( 'Test result = PASS')
      else
        WriteLn( 'Test result = FAIL')
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  WriteLn('Press Enter to close the program.');
  ReadLn;
end.

Listing Two: The fix

procedure TXXTEA_LargeBlock_LE_Decryptor.End_Decrypt;
var
  RequiredSizeDecrease: integer;
  L: integer;
  PlaintextArray, CiphertextArray: TLongWordDynArray;
begin
if FisBuffering then
    begin
    if FBufLen = 0 then exit;
//    c <= 211 ==>
//      2.2.1 Decrypt the message as one XXTEA block.
//      2.2.2 De-salt the decrypted plaintext. That is to say discard the last
//        8 bytes at the head of the decrypted plaintext.
//      2.2.3 De-pad the message out at the tail. The number of pad bytes to
//        remove is the value of the last byte.
    L := FBufLen div 4;
    SetLength( CiphertextArray, L); // Setup longword array.
    SetLength( PlaintextArray , L);
      begin
      // XXTEA only valid if blocksize is at least 2 longwords.
      // With the padding, this should ALWAYS be the case.
      // Otherwise the ciphertext message is invalid.
      Move( FBuffer[0], CiphertextArray[0], FBufLen); // Convert padded message to longwords.
      XXTEA_Decrypt( FKey.FNativeKey, CiphertextArray, PlaintextArray); // One-block encryption.
      end;
    FBufLen := L * 4;
    if FBufLen >= 8 then
        Dec( FBufLen, 8) // de-salt
      else
        FBufLen := 0;
    if FBufLen > 0 then  // Calculate pad.
        begin
        if Length( FBuffer) < FBufLen then
          SetLength( FBuffer, FBufLen);
        Move( PlaintextArray[0], FBuffer[0], FBufLen);
        RequiredSizeDecrease := FBuffer[FBufLen-1]
        end
      else
        RequiredSizeDecrease := 0;
    if FBufLen >= RequiredSizeDecrease then
        Dec( FBufLen, RequiredSizeDecrease) // de-pad
      else
        FBufLen := 0;
    if FBufLen > 0 then
      FPlainText.Write( FBuffer[0], FBufLen)
    end
  else
    begin
    FFixedDec.End_Decrypt;
    FOutputBuffer.EndStreaming; // Discard last 12 bytes
    FreeAndNil( FOutputBuffer)
    end;
FFixedDec := nil;
FFixedCipher := nil
end;

The next version will be 3.8.0 and will include the XXTEA fix and XXTEA unit tests. I can't give a schedule for it.

Sean B. Durkin
  • 12,659
  • 1
  • 36
  • 65
  • Thank you! Your solution works perfectly. I removed the GetIt! version of LockBox3, installed the Github version, patched TPLB3.XXTEA with your delta code, installed the package, and recompiled my test application as is. – Adriaan Jutte Aug 07 '17 at 14:05