Различные результаты, передающие system.string как параметр функции или константу

Я часами борюсь с очень странным явлением. Прежде всего, я использую библиотеку шифрования TurboPower LockBox v10 (с использованием TLbRijndael), но я Я не уверен, что виноват. Мне нужно передать ключ в функции шифрования / дешифрования, поэтому, естественно, я создал такие функции:

function EncryptText(const S: String; const Key: String): String;
function DecryptText(const S: String; const Key: String): String;

Как ни странно, хотя шифрование кажется работает, когда я пытаюсь расшифровать, он возвращает полный мусор. Итак, во время моего многочасового путешествия в поисках причины то, что я обнаружил, меня совершенно сбивает с толку. Когда я использую ключ шифрования, определенный как константа, все работает. Но при передаче того же значения (также 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;)

Что здесь не так и как решить?


person Jerry Dodge    schedule 13.01.2015    source источник
comment
Сначала это почти похоже на проблему с Unicode, но затем я дохожу до конца и вижу, что SetKey принимает нетипизированный параметр, а не _1 _...   -  person Mason Wheeler    schedule 14.01.2015
comment
Было бы здорово, если бы противников заставили написать комментарий, объясняющий их причину.   -  person Jerry Dodge    schedule 14.01.2015
comment
@Jerry: (Я не голосовал против). Голосование здесь анонимно, и требование комментария по какой-либо причине нарушит эту политику. Это также подробно обсуждалось в Meta Stack Overflow.   -  person Ken White    schedule 14.01.2015


Ответы (1)


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, что подтверждается третьей строкой.

Я хотел бы подчеркнуть важность различия между текстом и двоичным кодом.

person David Heffernan    schedule 13.01.2015
comment
Спасибо. Что касается вашего последнего комментария о двоичном формате, во-первых, это демонстрация, во-вторых, эта библиотека поддерживает множество типов, и я специально использую строковую часть (которая автоматически преобразует ее в двоичный) :-) - person Jerry Dodge; 14.01.2015
comment
Я действительно призываю вас перестать думать о шифровании как о чем-то, что работает с текстом. Он работает с двоичным кодом, используя двоичные ключи. Обязательно используйте кодировки Unicode, такие как UTF-8, и кодировки, такие как base64. Но держите это на переднем плане и в центре и не позволяйте скрывать его, как это кажется в настоящее время. - person David Heffernan; 14.01.2015
comment
Это очень понятно. Я не думаю об этом как о тексте, не понимаю, почему вы так думаете. Но для того, чтобы моя демонстрация была более удобной для пользователя, я сделал текстовый пример. Как я уже сказал, библиотека, которую я использую, поддерживает двоичный файл вместе со строкой, обернутой вокруг него. Кроме того, чтобы понять, почему я использую шифрование, оно в любом случае будет использоваться только как текст, так что это уровень, на котором я работаю. Вы хотите, чтобы я проигнорировал встроенное преобразование библиотеки и преобразовал ее сам? - person Jerry Dodge; 14.01.2015
comment
Я не думаю, что вы вообще понимаете мою точку зрения. Если вы хотите, чтобы я уточнил, я сделаю это. Но не раньше завтра. - person David Heffernan; 14.01.2015
comment
Вы намеревались, чтобы ваш ключ был в кодировке UTF-16? Вы знаете, что каждый второй байт ключа равен нулю? Лично я бы никогда не стал использовать удобные функции, которые работают со строками. Полностью двоичный. Преобразование текста в двоичный код с UTF-8. Чтобы представить двоичный файл в текстовой форме, используйте base64. - person David Heffernan; 14.01.2015
comment
Я понял, я подумал, что вы говорите о фактических данных, которые шифруются и расшифровываются, но теперь я понимаю, что вы говорите о ключе. Извинения. - person Jerry Dodge; 14.01.2015
comment
Решено преобразованием ключа в тип TKey256 библиотеки и его передачей. - person Jerry Dodge; 14.01.2015
comment
@Jerry - Также можно передавать символьные данные строки, например SetKey (Key [1]). - person Sertac Akyuz; 14.01.2015
comment
Я тоже говорю о данных - person David Heffernan; 14.01.2015