TStream von Speicher zu Speicher

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Nimral
Beiträge: 85
Registriert: Mi 10. Jun 2015, 11:33

TStream von Speicher zu Speicher

Beitrag von Nimral »

Hi,

ich habe eine Weile mit der Blowfish Library experimentiert. Neben einer Encrypt und Decrypt Methode für einen 64-Bit Block gibt es dort noch eine Implementierung als TStream über TBlowFishEncryptStream und TBlowFishDecryptStream. Ich habe damit ein wenig experimentiert und es klappt sehr gut mit Dateien.

Jetzt frage ich mich, ob ich dasselbe Interface auch nützen kann, um direkt aus dem Speicher zu lesen und in den Speicher zu schreiben statt in eine Datei. Ich habe hier einige Records im Speicher zu ver- und entschlüsseln.

Danke für eure Zeit,

Armin.

Socke
Lazarusforum e. V.
Beiträge: 2883
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: TStream von Speicher zu Speicher

Beitrag von Socke »

Hierzu hast du mit TCustomMemoryStream bereits eine Vorlage. Leite davon eine eigene Klasse ab und nutze setze einen Pointer auf deinen Record.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Nimral
Beiträge: 85
Registriert: Mi 10. Jun 2015, 11:33

Re: TStream von Speicher zu Speicher

Beitrag von Nimral »

Hi Socke,

danke für den Tipp. Wenn ich allerdings die bestehende TBlowFish...Stream Klasse nicht irgendwie für meine Zwecke einspannen kann, lohnt sich die Implementierung der Verschlüsselung als Stream nicht. Bringt mir nur Overhead und keinen Nutzen.

HG, Armin.

Socke
Lazarusforum e. V.
Beiträge: 2883
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: TStream von Speicher zu Speicher

Beitrag von Socke »

Nimral hat geschrieben:
Mo 22. Mär 2021, 20:11
danke für den Tipp. Wenn ich allerdings die bestehende TBlowFish...Stream Klasse nicht irgendwie für meine Zwecke einspannen kann, lohnt sich die Implementierung der Verschlüsselung als Stream nicht. Bringt mir nur Overhead und keinen Nutzen.
Deine Anfrage war ein wenig vage, was du wo einsparen willst. Mit Streams kannst du ganz famose Dinge anstellen. Falls du aktuell immer alles erst in eine Datei speicherst, lässt sich das ganz wunderbar durch TMemoryStream oder TBytesStream direkt in den RAM leiten.

Soweit ich die Dokumentation von z.B. TBlowFishDecryptStream verstehe, gibst du immer einen Stream an, in den (hier die entschlüsselten) Daten geschrieben werden.
Hier kannst du anstelle eines TFileStreams auch die oben genannten Streamklassen nutzen. Da TBlowFishDecryptStream selbst von TStream abgeleitet ist, kannst du auch einen kompletten Eingabestream per CopyFrom() verarbeiten.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Nimral
Beiträge: 85
Registriert: Mi 10. Jun 2015, 11:33

Re: TStream von Speicher zu Speicher

Beitrag von Nimral »

Nun ja, ich wollte einen Record oder einen String im Speicher verschlüsseln, ohne Umweg über eine Datei.

Inzwischen habe ich das so gut ich konnte und nach tapferem Kampf gegen den Typecheck von Pascal so halbwegs hingedengelt, aber noch ein Problem damit: bei Strings deren Länge kein Vielfaches von 8 Bytes (das ist die Blocksize von Blowfish) ist, werden die "überstehenden" Zeichen zerschossen.

Ich habe das Problem inzwischen in der Englischen Community gepostet, und in Stack Overflow, vielleicht magst Du dort mal reinschauen, dann muss ich den ganzen Summs nicht nochmal schreiben?

https://stackoverflow.com/questions/667 ... ge-problem
https://forum.lazarus.freepascal.org/in ... 838.0.html

Danke aber schon mal für Dein Interesse und Deine Hilfsbereitschaft,

Armin.

Socke
Lazarusforum e. V.
Beiträge: 2883
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: TStream von Speicher zu Speicher

Beitrag von Socke »

Nimral hat geschrieben:
Di 23. Mär 2021, 16:52
Inzwischen habe ich das so gut ich konnte und nach tapferem Kampf gegen den Typecheck von Pascal so halbwegs hingedengelt, aber noch ein Problem damit: bei Strings deren Länge kein Vielfaches von 8 Bytes (das ist die Blocksize von Blowfish) ist, werden die "überstehenden" Zeichen zerschossen.
Die Ursache wird vermutlich in deinem eigenen Code liegen. Die Klassen arbeiten wunderbar in beide Richtungen.

Soweit ich deinen Code richtig verstehe, willst du eine den Inhalt des Speichers durch die verschlüsselten Text inplace ersetzen. Das funktioniert nur, wenn dein Speicher (Record, String etc.) ein Vielfaches der Blowfish-Blockgröße groß ist. Wenn dieser kleiner ist, musst du die fehlenden Bytes mit 0 auffüllen (vgl. TBlowFishEncryptStream.Flush).
Bei einem String geht das vergleichsweise einfach. Beim Entschlüssel musst du dann entsprechend die schließenden Nullen entfernen.

Code: Alles auswählen

procedure ExtendStringToBFBlockSize(var s: String);
var
  BlockCount, FilledLength: SizeInt;
begin
  FilledLength := Length(s);
  BlockCount := Length(s) div SizeOf(TBFBlock);
  if (Length(s) mod SizeOf(TBFBlock)) > 0 then
  begin
    BlockCount := BlockCount + 1;
    SetLength(s, Blockcount * SizeOf(TBFBlock));
    FillChar(s[FilledLength + 1], Length(s) -  FilledLength, 0);
  end; // wenn der String ein Mehrfaches der Blockgröße lang ist, muss nichts gemacht werden
end;
Bei Records müsstest du einen ähnlichen Effekt per Compiler-Direktive {$PackRecords 8} erreichen können. Details dazu können andere aber besser erklären.

Wenn das gegeben ist, kann man die Prozedur BlowfishEncrypt viel simpler strukturieren:

Code: Alles auswählen

Procedure BlowfishEncrypt(
  var Contents;
  // SizeInt kann auf 64-Bit-Systemen auch große Datenmengen verarbeiten
  ContentsLength:SizeInt;
  // der Schlüssel wird nicht geändert, daher const
  const Key:String);

type
  PBFBlock = ^TBFBlock;
var
  objBlowfish: TBlowFish;
  FullBlocksCount: SizeInt;
  BlockIndex: Integer;
begin
  // Schlüssellänge prüfen, vgl. TBlowFishStream.Create
  if Length(Key) = 0 then
    EBlowFishError.Create('Empty passphrase is not allowed in constructor');
    
  if (ContentsLength mod SizeOf(TBFBlock)) <> 0 then
    raise EBlowFishError.Create('Input buffer must be multiple of block size.');
    
  // direkter Typecast ohne Umweg über eine Variable
  objBlowfish := TBlowFish.Create(PBlowFishKey(@Key[1])^, length(Key));

  FullBlocksCount := ContentsLength div SizeOf(TBFBlock);
  for BlockIndex := 0 to FullBlocksCount do
    objBlowfish.Encrypt(PBFBlock(@Contents)[BlockIndex]);

  FreeAndNil(objBlowfish);
end;
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Nimral
Beiträge: 85
Registriert: Mi 10. Jun 2015, 11:33

Re: TStream von Speicher zu Speicher

Beitrag von Nimral »

Socke hat geschrieben:
Di 23. Mär 2021, 20:54
Soweit ich deinen Code richtig verstehe, willst du eine den Inhalt des Speichers durch die verschlüsselten Text inplace ersetzen.
So war der Plan.

Die Idee war, und deshalb habe ich den Inhalt durch die Variable "BlowfishBlock" geschoben, bei den letzten Bytes nur so viel aus verschlüsselten Blowfishblock zu nehmen wie nötig ( count < 8 ). Mein Denkfehler war vermutlich anzunehmen, dass die verschlüsselten Bits eines Bytes irgendwie an der gleichen Stelle liegen wie der Klartext. Nach Studium des Codes komme ich eher zu dem Schluss, dass die Bits, die ehemals zu einem bestimmten Byte gehörten, querbeet durch den BlowfishBlock verteilt werden, wenn ich dann den BlowfishBlock abschneide auf das was in den Speicherplatz des Originals passt fehlen Sie bei der Wiederherstellung.

Ich werde also wohl oder übel Deiner Strategie folgen müssen, und Vorsorge treffen, dass die zu verschlüsselnden Strings und Records tatsächlich an 64 Bit Grenzen enden. Bei Records ist das kein Problem, bei Strings wirds lästig. Mit Nullen padden, hm, da bin ich nicht so begeistert. Mal sehen ob ich zu einem besseren Ergebnis komme wenn ich versuche, eine Längenangabe (wie das weiland bei ShortString vorhanden war) mitzunehmen durch die Verschlüsselung.

HG, Armin.

Socke
Lazarusforum e. V.
Beiträge: 2883
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: TStream von Speicher zu Speicher

Beitrag von Socke »

Nimral hat geschrieben:
Di 23. Mär 2021, 22:31
[Ich werde also wohl oder übel Deiner Strategie folgen müssen, und Vorsorge treffen, dass die zu verschlüsselnden Strings und Records tatsächlich an 64 Bit Grenzen enden.
Auch wenn ich nicht so tief in den eigentlichen Kryptoalgorithmen bewandert bin, ist das hier die grundsätzliche Funktionsweise des Algorithmus. Das trifft auf alle Block-Chiffren zu, wenn du später mal auf Twofish oder AES wechseln möchtest, hast du andere Block-Größen.
Per {$PACKRECORDS nn} kannst du maximal 32 Byte (256 Bit) vorgeben. Damit kannst du auch Blockgrößen neuerer Algorithmen abdecken, erkaufst dir dies aber durch einen Nachteil bei jeder Record-Zuweisung und massiv höherem Speicherverbrauch bei kleinen Records.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Nimral
Beiträge: 85
Registriert: Mi 10. Jun 2015, 11:33

Re: TStream von Speicher zu Speicher

Beitrag von Nimral »

Hi,

ich habs im Moment mal pragmatsch gelöst. Wenn ContentLength kein Vielfaches von TBFBlockSize ist, Exception und raus. Ich muss mich dann vorher drum kümmern was ich reinstopfe. Und mir gleichzeitig einen Merker gesetzt, den BlowFishEncrypt und Decrypt Streams etwas genauer auf die Finger zu schauen. Wenn dort das selbe Problem auftritt, müsste jeder Stream auf ein Vielfaches von 64 Bit aufgeblasen werden. Meinstens wird das nicht groß stören, aber was passiert bei einem "File of ...", wenn der Record kleiner als 64 Bits ist, also z.B. bei einem File of Byte? Ich vermute, dass dann am Ende zufällige Bytes hinzugefügt werden. Ist aber im Moment für mich irrelevant.

Herzlichen Dank nochmal für die Hilfe,

Armin.

Socke
Lazarusforum e. V.
Beiträge: 2883
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: TStream von Speicher zu Speicher

Beitrag von Socke »

Nimral hat geschrieben:
Mi 24. Mär 2021, 10:02
Wenn dort das selbe Problem auftritt, müsste jeder Stream auf ein Vielfaches von 64 Bit aufgeblasen werden.
TBlowFishEncrypStream.Flush macht genau das. Die Methode wird automatisch im Destruktor aufgerufen.
Nimral hat geschrieben:
Mi 24. Mär 2021, 10:02
Meinstens wird das nicht groß stören, aber was passiert bei einem "File of ...", wenn der Record kleiner als 64 Bits ist, also z.B. bei einem File of Byte? Ich vermute, dass dann am Ende zufällige Bytes hinzugefügt werden. Ist aber im Moment für mich irrelevant.
Solange du die Rückgabelänge kennst (Records) oder wie bei Strings schließende Nullen entfernt werden können, ist das unproblematisch. Wenn du aber die tatsächliche Länge nicht kennst, und Null ein gültiger Wert ist, hast du ein Problem. Dann musst du die Länge zusammen mit dem verschlüsselten Text oder als Bestandteil davon speichern.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Antworten