Blowfish Verschlüsselung falsch?

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Antworten
compmgmt
Beiträge: 351
Registriert: Mi 25. Nov 2015, 17:06
OS, Lazarus, FPC: Win 10 Pro | Lazarus 1.8.2 | FPC 3.0.4
CPU-Target: i386 + x86_64
Wohnort: in der Nähe von Stuttgart
Kontaktdaten:

Blowfish Verschlüsselung falsch?

Beitrag von compmgmt »

Guten Tag allerseits,

für ein kleines Vier Gewinnt Multiplayerspiel nutze ich Blowfish um die Kommunikation zu verschlüsseln. Von Server zu Client und umgekehrt funktioniert das ganze (Beide in Pascal geschrieben). Nun möchte ich aber einen Bot in Python schreiben, gegen den man spielen kann. Wenn ich mit Python oder über https://webnet77.net/cgi-bin/helpers/blowfish.pl den String 'Hallo123' mit dem Schlüssel '01234567' verschlüssele, kommt beides mal das gleiche raus. Nur mit Lazarus kommt was anderes raus.

Der Schlüssel ändert sich natürlich bei jeder Sitzung, zu diesem Test verwende ich aber einen festen Key. Als Beispiel möchte ich den String 'Hallo123' verschlüsseln. Mit Lazarus sieht das ganze so aus:

Code: Alles auswählen

uses
  ..., BlowFish;
 
function EncryptString(const Key, s: String): String;
var
  StringStream: TStringStream;
begin
  if (Key <> '') and (s <> '') then begin
    try
      StringStream := TStringStream.Create('');
      try
        with TBlowFishEncryptStream.Create(Key, StringStream) do begin
          WriteAnsiString(s);
          Free;
        end;
        Result := StringStream.DataString;
      except
        Result := '';
      end;
    finally
      FreeAndNil(StringStream);
    end;
  end
  else Result := '';
end;
 
var
  Key, Encrypted: String;
  c: Char;
 
begin
  Key := '01234567';
  WriteLn(Key);
  Encrypted := EncryptString(Key, 'Hallo123');
  for c in Encrypted do begin
    Write('0x' + IntToHex(Ord(c), 2) + ' ');
  end;
  WriteLn;
  ReadLn;
end.


Ergebnis vom Python programm und von https://webnet77.net/cgi-bin/helpers/blowfish.pl:

Code: Alles auswählen

0xDB 0x40 0xD9 0x82 0xB0 0x7E 0x6A 0x87

Ergebnis vom Lazarus Programm:

Code: Alles auswählen

0x9C 0x73 0x7C 0x5B 0xEA 0x7D 0x6A 0xC9 0xE5 0x9B 0x37 0x4E 0xC9 0xEE 0xE5 0x52


Was mache ich falsch?

Code: Alles auswählen

InitiateSystemShutdownExA(nil, nil, 0, true, false, $0005000F);
Have fun with this snippet ;)

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: Blowfish Verschlüsselung falsch?

Beitrag von theo »

Schon mal damit probiert? http://wiki.freepascal.org/DCPcrypt

Da gibt es versch. Ciphermode: cmCBC, cmCFB8bit, cmCFBblock, cmOFB, cmCTR

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Blowfish Verschlüsselung falsch?

Beitrag von Warf »

Erstmal sollte auffallen das das Lazarus ergebnis 128 Bit also 2 Blocks ist, während es bei Python nur 64 bit also ein Block sind. Der Grund ist recht offensichtlich, beim Pascal Code wird anscheinend die Terminierende NULL mitverschlüsselt, wodurch aus Hallo123 (64 Bit / 1Block) 'Hallo123'#0 wird (72 bit -> benötigt 2 blocks)

Dann ist natürlich die Frage der Konfiguration, wie theo bereits geschrieben hat kennt Blowfish als Block Chiphre verschiedene Modes, z.B. CBC (chain block chipher), welches die einzelnen Blöcke miteinander verrechnet. Da kommt dann natürlich auch der Initialization Vector (IV) ins spiel, mit dem der erste Block verrechnet wird (jeder weitere wird mit dem vorrigen block verrechnet), d.h. nicht nur der Mode sonder auch der IV muss gleich sein. Schau am besten mal hier zum Thema modes.

Mehr fällt mir dazu aber auch nicht ein (so gut kenn ich mich damit nicht aus, und normalerweise verwende ich Rijandael aus DCP Crypt)

EDIT: Kanns eventuell auch sein das das passwort in Pascal wiederum 0 terminiert ist und in python nicht? würde sinn ergeben wenn das auch für die Message gilt

compmgmt
Beiträge: 351
Registriert: Mi 25. Nov 2015, 17:06
OS, Lazarus, FPC: Win 10 Pro | Lazarus 1.8.2 | FPC 3.0.4
CPU-Target: i386 + x86_64
Wohnort: in der Nähe von Stuttgart
Kontaktdaten:

Re: Blowfish Verschlüsselung falsch?

Beitrag von compmgmt »

Oh, das Thema scheint doch komplizierter zu sein als gedacht. Danke euch beiden, ich werde mich da erstmal reinlesen.

Code: Alles auswählen

InitiateSystemShutdownExA(nil, nil, 0, true, false, $0005000F);
Have fun with this snippet ;)

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Blowfish Verschlüsselung falsch?

Beitrag von Warf »

Ja, das ist immer doof wenn man verschiedene Programme oder Bibliotheken für das verwendet.

Grundsätzlich ist Blowfish so einfach das eigentlich jeder hier im Forum den Algorithmus in einer Stunde implementieren können sollte. Aber dann kommen so fragen auf wie: Wie werden Strings Dargestellt (Terminierende Null, Encoding, etc.) Wenn du z.B. einen Delphi UTF16 String nimmst wirst du was anderes rausbekommen als bei nem Lazarus UTF-8 oder gar AnsiString. Python verwendet soweit ich weiß UTF-8 Strings die nicht 0 terminiert sind. Dann ist die Endianess auch noch eine Frage, soweit ich weiß wird beim Blowfish standard davon ausgegegangen das intern mit Big endian Zahlen gerechnet wird, aber ich bin mir nicht sicher ob es für die Eingabe eine feste definition gibt. z.B. der String 'ABCD' ist das jetzt die 32 bit zahl $41424344 oder $44434241? das sind natürlich komplett unterschiedliche Zahlen, somit kommt bei den internen Additionen dann auch was anderes raus. (Zur info, intern werden strings von Verschlüsselungsalgorithmen einfach als 32 oder 64 bit zahlen verwendet mit denen gerechnet wird). Ich weiß nicht an wie vielen Stellen echte Arithmetik passiert und keine Bit Operationen (das letzte mal als ich mir ne echte Implementation einer Block cipher angesehen hab ist schon ein paar Jahre her, und auch damals hab ich mich mit solchen details nicht beschäftigt), aber überall wo gerechnet wird muss natürlich auf die Endianess geachtet werden. (Das ist zwar nicht direkt dein Problem, aber es kann zu einem problem werden).

Die Parameter wie der IV oder der Mode müssen auf jeden fall übereinstimmen, die sollte man aber recht einfach setzen können

Ich würde dir raten einfach mal zu garantieren das die selben Bytes eingelesen werden, sowohl bei Python als auch bei Pascal (also gib z.B. statt nem string einfach nen Byte array mit bekannten werten für Passwort und Message an), denn auf identische String darstellung würde ich mich nicht verlassen (wie gesagt, wenn du den selben Code nach Delphi porten würdest hättest du UTF-16 an der Backe, und auch bei Lazarus kannst du die Code page einfach ändern. Der selbe code erzeugt also nicht immer den selben String). Wenns dann immer noch nicht übereinstimmt musst du mal schauen wie man die Parameter IV und Mode beeinflusst.

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Blowfish Verschlüsselung falsch?

Beitrag von Timm Thaler »

Oh man, ich weiss schon warum ich einfach mit Rot13 verschlüssele, und zweimal angewandt ist dass auch gleich doppelt sicher.

compmgmt
Beiträge: 351
Registriert: Mi 25. Nov 2015, 17:06
OS, Lazarus, FPC: Win 10 Pro | Lazarus 1.8.2 | FPC 3.0.4
CPU-Target: i386 + x86_64
Wohnort: in der Nähe von Stuttgart
Kontaktdaten:

Re: Blowfish Verschlüsselung falsch?

Beitrag von compmgmt »

@Warf Hättest du vielleicht ein kleines Beispiel wie man es mit DCPCrypt korrekt machen kann. Hab leider nix gefunden.

Code: Alles auswählen

InitiateSystemShutdownExA(nil, nil, 0, true, false, $0005000F);
Have fun with this snippet ;)

compmgmt
Beiträge: 351
Registriert: Mi 25. Nov 2015, 17:06
OS, Lazarus, FPC: Win 10 Pro | Lazarus 1.8.2 | FPC 3.0.4
CPU-Target: i386 + x86_64
Wohnort: in der Nähe von Stuttgart
Kontaktdaten:

Re: Blowfish Verschlüsselung falsch?

Beitrag von compmgmt »

Warf hat geschrieben:EDIT: Kanns eventuell auch sein das das passwort in Pascal wiederum 0 terminiert ist und in python nicht? würde sinn ergeben wenn das auch für die Message gilt
Ich übertrage ja nicht das Passwort sondern den String des SHA256 des Passworts. Ebenso speichere ich auch diesen in der Nutzerdatenbank. Allerdings habe ich mir für die gesamte Übertragung ein eigenes JSON-ähnliches Datenformat geschrieben, bei dem ein Authentifikationsversuch des Clients so aussieht:

Code: Alles auswählen

Auth(Name("Spieler"),Hash(A665A45920422F9D417E4867EFDC4FB8A04A1F3FFF1FA07E998E86F7F7A27AE3)) //Hash des Passworts "123"
Und diesen ganzen String ver-/entschlüssele ich.


So weit bin ich mit Python aber noch gar nicht gekommen, da es ja scheinbar eine unterschiedliche Verschlüsselungsroutine bei den beiden ist.

Nachtrag: Das Datenformat nutzt ausschließlich AnsiStrings und übertragen wird das ganze über lNet mittels TLSocket.SendMessage()

Code: Alles auswählen

InitiateSystemShutdownExA(nil, nil, 0, true, false, $0005000F);
Have fun with this snippet ;)

compmgmt
Beiträge: 351
Registriert: Mi 25. Nov 2015, 17:06
OS, Lazarus, FPC: Win 10 Pro | Lazarus 1.8.2 | FPC 3.0.4
CPU-Target: i386 + x86_64
Wohnort: in der Nähe von Stuttgart
Kontaktdaten:

Re: Blowfish Verschlüsselung falsch?

Beitrag von compmgmt »

Also langsam verzweifle ich. Ich hab es jetzt auch mal mit rijndael probiert und es geht immer noch nicht. Ich hab auf https://www.tools4noobs.com/online_tools/encrypt/ folgende Daten verwendet:

Code: Alles auswählen

Key: "01234567"
Data: "Hallo123"
Algorithm: "Rijndael-128"
Mode: "CBC"

Als Hexadezimal kodierten Output erhalte ich:

Code: Alles auswählen

0xFA 0x04 0x0C 0xFA 0x37 0xB2 0xB8 0x52 0x45 0x52 0x07 0x60 0x35 0x71 0xBC 0x75


Nun habe ich auf Basis dieses stackoverflow Beitrags (https://stackoverflow.com/questions/4234662/decrypting-using-rijndael-dcpcrypt-library-delphi) es auch mal probiert:

Code: Alles auswählen

function Encrypt(const Key: AnsiString): TBytes;
var
  Cipher: TDCP_rijndael;
  InData, OutData: TBytes;
begin
  Cipher := TDCP_rijndael.Create(nil);
  Cipher.Init(Key, Length(Key) * 8, nil);
  Cipher.CipherMode := TDCP_ciphermode.cmCBC;
  SetLength(InData, 8);
  InData[0] := Ord('H');
  InData[1] := Ord('a');
  InData[2] := Ord('l');
  InData[3] := Ord('l');
  InData[4] := Ord('o');
  InData[5] := Ord('1');
  InData[6] := Ord('2');
  InData[7] := Ord('3');
  SetLength(OutData, Length(InData));
  Cipher.EncryptCBC(InData[0], OutData[0], Length(InData));
  Result := OutData;
  Cipher.Burn;
  Cipher.Free;
end;
 
var
  Key: String;
  Encrypted: TBytes;
  b: Byte;
 
begin
  Key := '01234567';
  WriteLn(Key);
  Encrypted := Encrypt(Key);
  for b in Encrypted do begin
    Write('0x' + IntToHex(Ord(b), 2) + ' ');
  end;
  WriteLn;
  ReadLn;
end.

Das Ergebnis ist allerdings:

Code: Alles auswählen

0x30 0xC9 0xCA 0x0C 0xD3 0x80 0x73 0x40


Für mich sieht es gleich aus. Ich habe absichtlich keinen IV verwendet, da es auf der Website nicht möglich ist einen anzugeben.

Nachtrag: Mit "es sieht gleich aus" meine ich natürlich nicht das Ergebnis, sondern sämtliche Parameter der Verschlüsselung.

Code: Alles auswählen

InitiateSystemShutdownExA(nil, nil, 0, true, false, $0005000F);
Have fun with this snippet ;)

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Blowfish Verschlüsselung falsch?

Beitrag von mschnell »

Wieso ist der Key ein String ?

Wenn du unterschiedliche Programmier-Sprachen verwendest, handelst Du Dir da Kompatibilitäts-Probleme ein. Sogar zwischen verschiedenen Delphi-Versionen, zwischen verschiedenen FPC Versionen und zwischen Delphi und FPC, obwohl alle denselben Quelltext übersetzen. "AnsiString" nützt auch nichts, das ist - wenn ich mich recht erinnere - nur "String(CP_Default)" also dasselbe wie "String" ohne Type-Erweiterung.

Ich vermute ein Array von Bytes ist immer kompatibel zwischen den Sprachen.

-Michael
Zuletzt geändert von mschnell am Mi 13. Feb 2019, 14:23, insgesamt 2-mal geändert.

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Blowfish Verschlüsselung falsch?

Beitrag von Warf »

compmgmt hat geschrieben:Das Ergebnis ist allerdings:

Code: Alles auswählen

0x30 0xC9 0xCA 0x0C 0xD3 0x80 0x73 0x40


Jetzt bin ich überfragt, das sind 8 Byte, also 64 Bit. Rijandael/AES benutzt aber entweder 128 oder 256 bit Block size. Das kann eigentlich keine AES ausgäbe sein, außer es wurden irgendwelche Tricks verwendet (z.B. XOR multiplexing), da bin ich aber komplett überfragt

Antworten