无法使用 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 单元测试。我无法给出时间表。
我目前正在测试 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 单元测试。我无法给出时间表。