Recent

Author Topic: Blowfish procedures to encrypt (working) and decrypt (not working)  (Read 22045 times)

Leledumbo

  • Hero Member
  • *****
  • Posts: 8814
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Blowfish procedures to encrypt (working) and decrypt (not working)
« Reply #15 on: June 05, 2014, 06:30:30 pm »
@Leledumbo:  Sorry if don't agree with you...

I still think that storing binary data into strings is a very bad habit (especially with Pascal).

With that, you can encounter tons of unexpected issues, depending of the compiler, the OS, ...

Furthermore, are these "strings" supposed to be stored in a database ? Or in a text or an .ini file ? Or may be just passed to OS APIs or third party DLLs ?
I have no idea, the blog post talks in general. I think it's OK to store binary data in strings. Mention some of the issues because I never face any, especially the OS/compiler dependent ones.
For instance, having only a quick look at robert83a1's code, it seems to me that his main problem is:
Code: [Select]
...
Edit3.Text:=crypted;
...
Decrypt(Edit3.Text, ...
...

Why ? Because of OS APIs, of course.
Could you elaborate more? What does OS API have to do with this?
Your blog sample is working because there are no operations with the encrypted string between your encryption step and your decryption step. Oh and BTW, even with your own blog sample have you really tested if decrypted string = initial string ? Like this, I mean (strict string equality) : WriteLn('Equality='+BoolToStr(temp=value));

It's working: great. Just make a test with another one, please. Let's say, for instance : value := 'this is another string';

Is it still working ? I guess it shouldn't (i.e. length(temp) <> length(value)). Why ? Because Blowfish uses 64-bit block size.
Yep, it's still working. No problem with 64-bit block size, original data length should have no limitation (other than memory limit), regardless block size. Try this program:
Code: [Select]
{$mode objfpc}{$H+}

uses
  Classes,
  BlowFish;

const
  ValueCount = 5;
  Values: array [1..ValueCount] of record
    Key: String;
    Str: String;
  end = (
    (Key:'testkey';Str:'this is a string'),
    (Key:'testkey';Str:'this is another string'),
    (Key:'anotherkey';Str:'this is a string'),
    (Key:'anotherkey';Str:'this is another string'),
    (Key:'@!#$%^&*()';Str:')(*&^%$#@!')
  );
var
  en: TBlowFishEncryptStream;
  de: TBlowFishDeCryptStream;
  s1,s2: TStringStream;
  i: Integer;
  key,value,temp: String;
begin
  for i := Low(Values) to High(Values) do begin
    key := Values[i].Key;
    value := Values[i].Str;
    WriteLn('original : ' + value);

    s1 := TStringStream.Create('');
    en := TBlowFishEncryptStream.Create(key,s1);

    en.WriteAnsiString(value);
    en.Free;
    WriteLn('encrypted: ' + s1.DataString);

    s2 := TStringStream.Create(s1.DataString);
    s1.Free;

    de := TBlowFishDeCryptStream.Create(key,s2);
    temp := de.ReadAnsiString;
    WriteLn('decrypted: ' + temp);

    WriteLn('are strings strictly equal? ',value = temp);
    WriteLn;
    de.Free;
    s2.Free;
  end;
end.
Do they all say TRUE?

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Blowfish procedures to encrypt (working) and decrypt (not working)
« Reply #16 on: June 05, 2014, 10:22:36 pm »
OK found the second link I was talking about http://www.wolfgang-ehrhardt.de/crypt_en.html#blowfish I haven't tested neither of them yet but from the site it looks like the guy knows what he is doing. So it is on my to do list to test them and compare them with the existing dpc and the build in fpc implementation at some point.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: Blowfish procedures to encrypt (working) and decrypt (not working)
« Reply #17 on: June 06, 2014, 05:12:13 pm »
@Leledumbo:

Preliminary: I'm speaking here only for the Windows OS.


As long as the strings which contain binary data are only used as a "black box", I guess there are no risks for the binary data to be modified/corrupted.

But, when these strings are used as "strings", I mean when they are used with procedures/functions expecting "real" strings for instance, you can experiment any kind of troubles.

I guess -most of- the RTL functions are probably OK (though I've never made any tests; furthermore the 2.7.x branch is another matter), but it's not the case with LCL ones which expect UTF8 strings by default.

Furthermore, Windows is using internally null terminated strings. If there is null char inside you binary data, and if you pass the corresponding string to a Windows API, your "string" will be truncated to the part being only before the null char.

Sample for a form with an edit control (Edit1) and a button control (Button1):
Code: [Select]
procedure TForm1.Button1Click(Sender: TObject);
var test: String;
begin
  test:='begin'+Chr(0)+'end';
  ShowMessage('Before: Len='+IntToStr(Length(test)));   // Len=9 (Whole test string)
  Edit1.text:=test;
  test:=Edit1.text;
  ShowMessage('After: Len='+IntToStr(Length(test)));    // Len=5 (Only 'begin'),
end;


robert83a1's first sample was potentially facing these 2 problems: i.e. LCL modification, plus Windows API truncation.


Attached a quick (and "dirty") program sample, in order to illustrate what I mean. But any other kind of sample altering string data could do the trick: as mentioned before, storing these strings in a text field inside a database, or in an external text file, and so on...

I've taken exactly your blog sample source code, and modified it for my own purposes (i.e. my modifications are commented with "// Modif", to spot them easily).

This sample does exactly what you've coded: it takes a string, encrypts it and decrypts it (using the BlowFish algorithm and a fixed key). Enter a text into the 'Input' edit box, press 'Test OK', and it should works properly in all cases ('Test OK' could be considered more or less as exactly your own sample).

I've just introduced an optional additional step with 2 possible cases. It's only this part of my code which is interesting for my illustration ('StrEnc' is a string variable which is containing the encrypted string):
Code: [Select]

  case CallOption of
  1:      // LCL+Windows APIs
    begin
      Form1.Edit2.Caption:=StrEnc;
      StrEnc:=Form1.Edit2.Caption;
    end;
  2:      // Windows APIs only
    begin
      SendMessageA(Form1.Edit2.Handle, WM_SETTEXT, 0, NativeInt(StrEnc));
      i1:=SendMessageA(Form1.Edit2.Handle, WM_GETTEXTLENGTH, 0, 0);
      if i1=0 then StrEnc:=''
      else begin
        Inc(i1);
        SetLength(StrEnc, i1);
        SetLength(StrEnc, SendMessageA(Form1.Edit2.Handle, WM_GETTEXT, i1, NativeInt(StrEnc)));
      end;
    end;
  // Else, None
  end;                                           


-Test OK (CallOption=0): the output encrypted string is stored inside another string (StrEnc), and this other string is used as an input for the decoding step. As mentioned before, it should work properly in any cases (this is what you have tested and mentioned). So, storing these binary data from a string to another one is not a problem by itself. Please note however that the encrypted string displayed into the corresponding edit control (i.e. Edit2) may be incomplete if a null char is present,

-Test KO (CallOption=1): the string receiving the encrypted string is now stored in a edit control, and get back from it (via the LCL). It's supposed to behave as robert83a1's first sample. Here, both the LCL and Windows are interfering, and it should not work in most of the cases (wrong final decrypted string, stream error, ...). Basically, the LCL is first converting the "UTF8 string" into an Ansi or Wide string before passing it to the concerned Windows API, in order to update the text in the edit control. Windows may eventually truncate it, if a null char is present. Finally, the LCL is converting back the edit control text from an Ansi or Wide string to an UTF8 string.

-Test API (CallOption=2): here, the LCL is not involved at all when updating/getting the text in/from the edit control, only the Windows API. So, the only possible change is coming from Windows, which may eventually truncates the string (i.e. if null char present). This one should work as long as there is no such null char inside the encrypted string (you can make a test first, with the 'Test OK' button).


Finally, a few input strings to test some different cases (don't enter the " character):

-"test": OK for 'Test OK', OK for 'Test API' (there is no null char inside the encrypted string), but KO for 'Test KO' (stream error because of the LCL conversions),

-"hello": OK for 'Test OK', KO for 'Test API' (first null char at position 3 in the encrypted string), and KO for 'Test KO' of course,

-"test2": idem as for "hello", except for 'Test API'. The test is still KO (i.e. null char) but there is no stream error; instead, decrypted string <> initial string,

-"r": OK in all cases (no null char and no LCL conversion troubles).


PS. Feel free to discuss and/or contest any part of my posts. I've already offended one person in this topic, and I'd prefer not to offend another one (one per topic is quite enough, I guess).
.

Leledumbo

  • Hero Member
  • *****
  • Posts: 8814
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Blowfish procedures to encrypt (working) and decrypt (not working)
« Reply #18 on: June 06, 2014, 07:24:38 pm »
OK, I get it. Encrypted data (when kept as strings) are not meant to be used like that indeed. If one is about to display the encrypted data, normally one would convert it first to hex string, which is then passed to the GUI control. It's simply an abuse IMHO.

Gizmo

  • Hero Member
  • *****
  • Posts: 831
Re: Blowfish procedures to encrypt (working) and decrypt (not working)
« Reply #19 on: June 07, 2014, 10:37:45 pm »
Dcpcfypt is a good library. I use it to hash data but it is equally designed for encryption and decryption

 

TinyPortal © 2005-2018