将 system.string 作为函数参数或常量传递的不同结果
Different results passing system.string as either function parameter or constant
我已经为一个非常奇怪的现象奋斗了几个小时。首先,我正在使用 TurboPower LockBox v10 加密库(使用 TLbRijndael
),但我不认为这是问题所在。我需要将一个键传递给 encrypt/decrypt 函数,所以我自然而然地创建了这样的函数:
function EncryptText(const S: String; const Key: String): String;
function DecryptText(const S: String; const Key: String): String;
奇怪的是,虽然加密 似乎 有效,但当我尝试解密时,它 returns 完全是垃圾。因此,在我 hours-long 寻找原因的旅程中,我发现的东西绝对让我感到困惑。当我使用定义为常量的加密密钥时,一切正常。但是传递完全相同的值(也 system.string
)作为函数中的参数,它不会。
总结一下:
- 将密钥作为常量传递时 (
system.string
),一切正常。
- 将密钥作为参数 (
system.string
) 传递时,结果随机且不正确(并且无法解密)。
这是我整理的演示程序(我的第三个测试应用程序试图解决这个问题):
program EncDecDemo;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.Classes, System.SysUtils,
LbCipher, LbClass, LbAsym, LbRSA;
const
ENC_KEY = 'abcdefghijklmnopqrstubwxyz123456';
var
EncComp: TLbRijndael;
function EncAsConst(const S: String): String;
begin
EncComp.SetKey(ENC_KEY); //<-- works as expected
Result:= EncComp.EncryptString(S);
end;
function DecAsConst(const S: String): String;
begin
EncComp.SetKey(ENC_KEY); //<-- works as expected
Result:= EncComp.DecryptString(S);
end;
function EncAsParam(const S: String; const Key: String): String;
begin
EncComp.SetKey(Key); //<-- produces random results
Result:= EncComp.EncryptString(S);
end;
function DecAsParam(const S: String; const Key: String): String;
begin
EncComp.SetKey(Key); //<-- produces random results
Result:= EncComp.DecryptString(S);
end;
var
InputStr: String;
EncrStr: String;
DecrStr: String;
Ctr: Integer;
begin
EncComp:= TLbRijndael.Create(nil);
try
EncComp.Encoding:= TEncoding.UTF8;
EncComp.KeySize:= ks256;
WriteLn('Enter a string to encrypt: ');
ReadLn(Input, InputStr);
WriteLn;
WriteLn('Encrypting as constants...');
EncrStr:= EncAsConst(InputStr);
DecrStr:= DecAsConst(EncrStr);
WriteLn('Pass 1: Enc = '+EncrStr+' Dec = '+DecrStr);
EncrStr:= EncAsConst(InputStr);
DecrStr:= DecAsConst(EncrStr);
WriteLn('Pass 2: Enc = '+EncrStr+' Dec = '+DecrStr);
EncrStr:= EncAsConst(InputStr);
DecrStr:= DecAsConst(EncrStr);
WriteLn('Pass 3: Enc = '+EncrStr+' Dec = '+DecrStr);
WriteLn;
WriteLn('Encrypting as parameters...');
EncrStr:= EncAsParam(InputStr, ENC_KEY);
WriteLn('Pass 1: Enc = '+EncrStr);
EncrStr:= EncAsParam(InputStr, ENC_KEY);
WriteLn('Pass 2: Enc = '+EncrStr);
EncrStr:= EncAsParam(InputStr, ENC_KEY);
WriteLn('Pass 3: Enc = '+EncrStr);
EncrStr:= EncAsParam(InputStr, ENC_KEY);
WriteLn('Pass 4: Enc = '+EncrStr);
EncrStr:= EncAsParam(InputStr, ENC_KEY);
WriteLn('Pass 5: Enc = '+EncrStr);
EncrStr:= EncAsParam(InputStr, ENC_KEY);
WriteLn('Pass 6: Enc = '+EncrStr);
WriteLn;
WriteLn('Press enter to exit');
ReadLn;
finally
EncComp.Free;
end;
end.
如您所见,当在函数中传递与参数完全相同的值时,每次都会产生不同的结果。让我重申一下,完全相同的值。
我唯一的结论是 system.string
在用作常量 (const ENC_KEY = 'value';
) 时在某种程度上与用作变量时在幕后的大小或类型不同。
当我查看 SetKey
过程内部时,它是一个简单的 Move
调用...
procedure TLbRijndael.SetKey(const Key);
begin
Move(Key, FKey, FKeySizeBytes);
end;
(其中 FKey
是 array [0..31] of Byte;
)
这里出了什么问题,如何解决?
procedure TLbRijndael.SetKey(const Key);
begin
Move(Key, FKey, FKeySizeBytes);
end;
你传递给这个无类型参数的就是问题所在。当您传递一个真正的常量时,将传递文本。当你传递一个字符串变量时,传递的是文本的地址,而不是文本本身。那是因为字符串变量实际上是指向文本的指针。
我相信您将需要停止使用字符串变量作为键并提供一个二进制缓冲区。
考虑这个说明问题的程序:
{$APPTYPE CONSOLE}
uses
SysUtils;
procedure Foo(const Key);
var
buff: Pointer;
begin
Move(Key, buff, SizeOf(buff));
Writeln(Format('%p', [buff]));
end;
const
ENC_KEY = 'abcdefghijklmnopqrstubwxyz123456';
var
str: string = ENC_KEY;
begin
Foo(ENC_KEY);
Foo(str);
Writeln(Format('%p', [Pointer(str)]));
end.
在我的机器上输出是:
00620061
00419D48
00419D48
第一行是 'ab'
编码为 UTF-16LE 的文本。第二行是str
的地址,第三行确认
我想强调区分文本和二进制文件的重要性。
我已经为一个非常奇怪的现象奋斗了几个小时。首先,我正在使用 TurboPower LockBox v10 加密库(使用 TLbRijndael
),但我不认为这是问题所在。我需要将一个键传递给 encrypt/decrypt 函数,所以我自然而然地创建了这样的函数:
function EncryptText(const S: String; const Key: String): String;
function DecryptText(const S: String; const Key: String): String;
奇怪的是,虽然加密 似乎 有效,但当我尝试解密时,它 returns 完全是垃圾。因此,在我 hours-long 寻找原因的旅程中,我发现的东西绝对让我感到困惑。当我使用定义为常量的加密密钥时,一切正常。但是传递完全相同的值(也 system.string
)作为函数中的参数,它不会。
总结一下:
- 将密钥作为常量传递时 (
system.string
),一切正常。 - 将密钥作为参数 (
system.string
) 传递时,结果随机且不正确(并且无法解密)。
这是我整理的演示程序(我的第三个测试应用程序试图解决这个问题):
program EncDecDemo;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.Classes, System.SysUtils,
LbCipher, LbClass, LbAsym, LbRSA;
const
ENC_KEY = 'abcdefghijklmnopqrstubwxyz123456';
var
EncComp: TLbRijndael;
function EncAsConst(const S: String): String;
begin
EncComp.SetKey(ENC_KEY); //<-- works as expected
Result:= EncComp.EncryptString(S);
end;
function DecAsConst(const S: String): String;
begin
EncComp.SetKey(ENC_KEY); //<-- works as expected
Result:= EncComp.DecryptString(S);
end;
function EncAsParam(const S: String; const Key: String): String;
begin
EncComp.SetKey(Key); //<-- produces random results
Result:= EncComp.EncryptString(S);
end;
function DecAsParam(const S: String; const Key: String): String;
begin
EncComp.SetKey(Key); //<-- produces random results
Result:= EncComp.DecryptString(S);
end;
var
InputStr: String;
EncrStr: String;
DecrStr: String;
Ctr: Integer;
begin
EncComp:= TLbRijndael.Create(nil);
try
EncComp.Encoding:= TEncoding.UTF8;
EncComp.KeySize:= ks256;
WriteLn('Enter a string to encrypt: ');
ReadLn(Input, InputStr);
WriteLn;
WriteLn('Encrypting as constants...');
EncrStr:= EncAsConst(InputStr);
DecrStr:= DecAsConst(EncrStr);
WriteLn('Pass 1: Enc = '+EncrStr+' Dec = '+DecrStr);
EncrStr:= EncAsConst(InputStr);
DecrStr:= DecAsConst(EncrStr);
WriteLn('Pass 2: Enc = '+EncrStr+' Dec = '+DecrStr);
EncrStr:= EncAsConst(InputStr);
DecrStr:= DecAsConst(EncrStr);
WriteLn('Pass 3: Enc = '+EncrStr+' Dec = '+DecrStr);
WriteLn;
WriteLn('Encrypting as parameters...');
EncrStr:= EncAsParam(InputStr, ENC_KEY);
WriteLn('Pass 1: Enc = '+EncrStr);
EncrStr:= EncAsParam(InputStr, ENC_KEY);
WriteLn('Pass 2: Enc = '+EncrStr);
EncrStr:= EncAsParam(InputStr, ENC_KEY);
WriteLn('Pass 3: Enc = '+EncrStr);
EncrStr:= EncAsParam(InputStr, ENC_KEY);
WriteLn('Pass 4: Enc = '+EncrStr);
EncrStr:= EncAsParam(InputStr, ENC_KEY);
WriteLn('Pass 5: Enc = '+EncrStr);
EncrStr:= EncAsParam(InputStr, ENC_KEY);
WriteLn('Pass 6: Enc = '+EncrStr);
WriteLn;
WriteLn('Press enter to exit');
ReadLn;
finally
EncComp.Free;
end;
end.
如您所见,当在函数中传递与参数完全相同的值时,每次都会产生不同的结果。让我重申一下,完全相同的值。
我唯一的结论是 system.string
在用作常量 (const ENC_KEY = 'value';
) 时在某种程度上与用作变量时在幕后的大小或类型不同。
当我查看 SetKey
过程内部时,它是一个简单的 Move
调用...
procedure TLbRijndael.SetKey(const Key);
begin
Move(Key, FKey, FKeySizeBytes);
end;
(其中 FKey
是 array [0..31] of Byte;
)
这里出了什么问题,如何解决?
procedure TLbRijndael.SetKey(const Key);
begin
Move(Key, FKey, FKeySizeBytes);
end;
你传递给这个无类型参数的就是问题所在。当您传递一个真正的常量时,将传递文本。当你传递一个字符串变量时,传递的是文本的地址,而不是文本本身。那是因为字符串变量实际上是指向文本的指针。
我相信您将需要停止使用字符串变量作为键并提供一个二进制缓冲区。
考虑这个说明问题的程序:
{$APPTYPE CONSOLE}
uses
SysUtils;
procedure Foo(const Key);
var
buff: Pointer;
begin
Move(Key, buff, SizeOf(buff));
Writeln(Format('%p', [buff]));
end;
const
ENC_KEY = 'abcdefghijklmnopqrstubwxyz123456';
var
str: string = ENC_KEY;
begin
Foo(ENC_KEY);
Foo(str);
Writeln(Format('%p', [Pointer(str)]));
end.
在我的机器上输出是:
00620061 00419D48 00419D48
第一行是 'ab'
编码为 UTF-16LE 的文本。第二行是str
的地址,第三行确认
我想强调区分文本和二进制文件的重要性。