无法使用 LockBox3 解密 XXTEA 加密字符串

Unable to decrypt XXTEA encrypted string using LockBox3

我目前正在测试 Lockbox3,在尝试使用 XXTEA 密码进行简单的字符串加密和解密时遇到了 运行 问题。我正在使用 Delphi 10.1 Berlin 并使用 GetIt 安装了最新版本的 Lockbox3!我的测试应用程序是针对 Win32 (VCL) 的。

问题是加密工作正常,但是当我尝试解密字符串时,抛出异常:"No mapping for the Unicode character exists in the target multi-byte code page."。我不确定是什么问题?

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;

有两个问题。首先是XXTEA解密器存在缺陷。如果您迫不及待地等待发布修复程序,我已经发布了一个代码增量,您可以应用它并重新编译以获得即时解决方案。我有点惊讶 XXTEA 没有单元测试。这个库中的其他所有内容都有大量的单元测试,所以我也将致力于此。

第二个问题是您在运行时设置的编解码器 属性 名称与设计时 属性 名称略有不同。 (ChainModeId 而不是 ChainMode)。不同之处在于使设计时 属性 配置更容易。

无论如何,我在 Lockbox-3 v3.7.0 上做了一些测试。可以在 http://lockbox.seanbdurkin.id.au/HomePage and the source code from https://github.com/SeanBDurkin/tplockbox 找到主页。我用 Delphi 10.2 Tokyo 进行了测试;目标=Win32.

清单一给出了测试程序(单文件控制台程序),清单二给出了修复程序。修复是对方法过程 TXXTEA_LargeBlock_LE_Decryptor.End_Decrypt() 的更新,可以在单元 TPLB3.XXTEA 中找到。 GetIt 版本的单位名称略有不同。 也就是说,相对于 XXTEA,等到单元测试发布后可能更安全。

此外,如果有人想通过贡献已知答案测试 (KAT) 来提供帮助,那对所有人都有好处。

清单一:测试程序

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.

清单二:修复

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;

下一个版本将为 3.8.0,并将包括 XXTEA 修复和 XXTEA 单元测试。我无法给出时间表。