Entwickler-Ecke

Sonstiges (Delphi) - Viginére-Codierung


Philipp_Frank - Mo 17.02.03 16:49
Titel: Viginére-Codierung
Weiß jemand was über die Viginére-Codierung? Soweit ich weiß, codiert die mit nem Passwort, also nem String den man eingibt, mit Hilfe der positionen der einzelnen Buchstaben. Wir machen das gerade in der Schule. Wir sollen Wege finden, einen Text zu verschlüsseln und wieder zu decodieren. Wenn mir jemand mit der Viginére helfen könnte einen text aus einem Memofeld zu codieren wäre das Klasse, sonst würd ich das per Matrix-Codierung machen, denke das bekomme ich gebacken.

Gruß aus Bielefeld, Philipp

Moderiert von user profile iconTino: Link entfernt.


AXMD - Mo 17.02.03 16:59

Hi,

ganz einfach: die Vinègre-Chiffre funktioniert wie die Caesar-Chiffre aufgrund der Tatsache, dass das Alphabet verschoben wird - bei Caesar jeweils um einen konstanten Wert, bei Vinègre um variable Werte. Am besten wäre es, wenn du einfach ein Array nimmst, das beispielsweise folgende Werte hat: 2, 2, 0, 5, 1, 9, 8, 6... Nur ein Beispiel. Das wäre dann ein const verschiebung: Array [1..8] of Byte = (... die Werte ...)
Nun musst du deinen Text nur noch zeilenweise als Strings behandeln und dann in einer For-Schleife die Zeichen jeweils um Position mod 8 verschieben. Dazu musst du Ord und Chr verwenden. Das wär's soweit. Wenn ich was Falsches gesagt habe, sagt es mir bitte - das war nur so aus dem Ärmel geschüttelt.

AXMD


Philipp_Frank - Mo 17.02.03 17:02
Titel: cool
das war ja schon ganz cool, mache jetzt delphi gerad ein halbes Jahr in der Schule, das war jetzt nen bissel kompliziert, kannstes mir nen bisschen ausführlicher erklären, bitte?


AXMD - Mo 17.02.03 17:14

Hi,

am besten wirst du es mit einem kleinen Stück Quelltext verstehen:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
EncodeLine(s: String): String;
var
  i: Integer;
  dummy: String;
begin
  dummy := s;
  for i := 1 to Length(s) do
    dummy[i] := Chr(Ord(dummy) xor verschiebung[i mod 8]);
  Result := dummy;
end;


Zur Info: in diesem Quelltextstück fehlen 2 Dinge: erstens das Konstanten-Array verschiebung und zweitens eine Methode für das ganze Memo. Letzteres ist einfach:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure EncodeMemo(Memo: TMemo);
var
  i: Integer;
  dummy: String;
begin
  for i := 0 to Memo.Lines.Count - 1 do begin
    dummy := Memo.Lines[i];
    dummy := EncodeLine(dummy);
    Memo.Lines[i] := dummy;
    end;
end;


Das Stück hier habe ich jetzt etwas vereinfacht - ginge auch komplizierter.
Muss jetzt leider gehen (bin @SCHOOL) und komme erst morgen wieder ins Internet. Wenn du solange warten kannst, kann ich dir eine ausführlichere Erklärung morgen liefern.
So long,
AXMD


Philipp_Frank - Mo 17.02.03 17:16

woul be nice


Anonymous - Mo 17.02.03 17:28

Moin!

Ich hab letztens als Facharbeit eine Enigma nachprogrammiert!
Vielleicht hilft dir dieser Link hier [http://www-ivs.cs.uni-magdeburg.de/bs/lehre/wise0102/progb/index.shtml] weiter!

Viel Spass!


Christian S. - Mo 17.02.03 18:28

Zitat:
Ich hab letztens als Facharbeit eine Enigma nachprogrammiert!
Na, vergisst Du da nicht zwei Leute?


Anonymous - Mo 17.02.03 20:20

Soll ich etwa jeden Furz erwähnen, der mir geholfen hat !? :twisted:

Schließlich hab ich jawohl den Hauptteil selbst geschrieben, ihr habt mir aber viel geholfen! ... Hab übrigens ne 1+ dafür bekommen! :lol: (Ich bin ja so gut!)


Christian S. - Mo 17.02.03 20:30

Zitat:
Soll ich etwa jeden Furz erwähnen, der mir geholfen hat !? :twisted:

Nur für die Leute, die mal sehen wollen, wie ein Furz hilft:
Teil 1 [http://www.auq.de/viewtopic.php?t=3665]
Teil 2 [http://www.delphi-forum.de/viewtopic.php?t=3773&highlight=enigma]

Zitat:
(Ich bin ja so gut!)
Das habe ich jetzt mal überlesen! :twisted:

MfG,
Peter

P.S: Nicht allzu ernst nehmen, ja? Nur ein bisschen! :wink:


Anonymous - Mo 17.02.03 20:43

Auch wenn das hier schon wieder zu einem Privatthread wird und es übelst OffTopic ist:

Ich weiss, dass ihr beide mir geholfen habt!
Aber ich hab auch viel alleine geschafft und wenn ich nunmal die ganzen Befehle nicht kenne (weil die uns in der Schule nicht beigebracht wurden), dann muss ich halt fragen. Und wenn ich frage, dann stell ich mich immer dumm an! :roll:

Haste ja gesehen, woll? :twisted:

Außerdem: Die 1 - 2mal, wo ihr mir geholfen habt, kann ich getrost vernachlässigen! :twisted:

P.S: Nicht allzu ernst nehmen, ja? Nur ein bisschen! :wink:


Christian S. - Mo 17.02.03 20:58

Zitat:
Außerdem: Die 1 - 2mal, wo ihr mir geholfen habt, kann ich getrost vernachlässigen!
:lol:

Um mal wieder zum Thread zurückzukehren: ich wüsste gerne, was das mit dem XOR auf sich hat. Ich verstehe nicht ganz, was der hier bewirken soll. Ich habe das mal ausprobiert und wenn das ganze wirklich nur eine Verschiebung sein soll, kommt nicht das heraus, was herauskommen soll. Oder ich stelle mich blöde an. Oder beides.

MfG,
Peter


AXMD - Di 18.02.03 08:42

Hi,

dieser Text ist von gestern, dem 17.2.2003. Ich weiß nicht, ob du inzwischen meine Ansätze verstanden hast - ansonsten glaube ich, hier sehr detailreich erklären zu können, wie die Vinègre-Chiffre (nicht nur vom Prinzip, sondern auch von der programmtechnischen Umsetzung her) funktioniert.
Ich habe mir viel Zeit für diesen Text gestern Abend genommen und hoffe, dass die Vinègre-Chiffre nun verständlich ist. Solltest du dennoch Fragen haben, kannst du mir entweder eine private Nachricht oder eine Email schreiben. Scheu dich nicht: jeder fängt mal an!

Zu allererst möchte ich eine kleine Korrektur einbringen: im Code-Abschnitt, in dem die Funktion "EncodeLine" besprochen wird, musst du statt dem "xor" ein "+" nehmen. Aber mehr dazu später.

Nun möchte ich noch einmal ganz von vorne beginnen: die Vinègre-Chiffre ist im Prinzip eine Erweiterung bzw. Verbesserung der Caesar-Chiffre. Die Caesar-Chiffre beruht auf einem sehr einfachen Prinzip: der zu kodierende Text wird zeichenweise um einen bestimmten Wert verschoben. Um das Ganze etwas zu veranschaulichen: der Text "Hallo" würde bei einer Verschiebung von 3 so aussehen: "Kdoor" - jedes Zeichen wird im Alphabet um jeweils drei Zeichen "erhöht".
Die Vinègre-Chiffre verfeinert diese Methode etwas, da die Caesar-Chiffre aufgrund der Häufigkeit der Buchstaben sehr leicht zu entschlüsseln ist (e ist im Deutschen der häufigste Buchstabe). Sehen wir uns also die Vinègre-Chiffre an: anstatt jedes Zeichen um den gleichen Wert zu erhöhen, werden die Zeichen in regelmäßigen Abständen um verschiedene Werte erhöht. Wieder ein Beispiel zur Veranschaulichung: "Hallo" würde bei einer Verschiebung von 3-2-1 zu "Kcmoq" werden. Um dem Ganzen folgen zu können: das "H" wird um 3 erhöht, also K. Das "a" wird um 2 erhöht, also "c". Das "l" wird um 1 erhöht, also m. Nun sind wir am Ende unseres Verschiebungsmusters angelangt. Was tun wir jetzt? Ganz einfach: wir beginnen einfach wieder von vorne: das zweite "l" wird also wieder um 3 erhöht und man erhält so ein "o". Das Setzen wir bis zum Ende des verschlüsselten Textes fort.

Das war soweit die Theorie, doch wie sieht's in der Praxis aus? Was wir auf jeden Fall überlegen müssen: wie soll der Text verschlüsselt werden (bzw. wieviele verschiedene aufeinanderfolgende Verschiebungen). Man könnte beispielsweise ein Array mit 8 Feldern nehmen. Das könnte so aussehen:

Quelltext
1:
2:
const
  verschiebungen: Array [1..8] of Byte = (2, 2, 0, 5, 1, 9, 8, 6);

Warum ich Byte gewählt habe, dürfte eigentlich klar sein: wenn wir Zahlen unter zehn verwenden, ist die Verwendung von Variablentypen, die größere Werte fassen können, absolut unnötig. Außerdem müssen wir beachten, dass bei einer Vinègre-Verschlüsselung keine allzu großen Werte verwendet werden dürfen. Der Grund ist eigtnlich offensichtlich: ein "normaler" String kann laut ASCII-Zeichensatz "nur" 255 verschiedene Zustände pro Zeichen annehmen. Wenn wir also zum 220. Zeichen 50 hinzuzählen, kämen wir auf 270. Und das lässt sich nicht mehr darstellen. Lassen wir die Zahlen hingegen klein, ist die Wahrscheinlichkeit, dass wir über diesen Wert 255 hinauskommen, relativ gering.
Um nun zum eigentlichen Thema zurückzukommen: unser zweites Problem ist die Zeilenanzahl des Memos. Wir müssen also jede Zeile einzeln kodieren. Wenn wir den ganzen Text kodieren würden, hätten wir ein Problem: der Wagenrücklauf #13 würde ebenfalls verschlüsselt werden - dann hätten wir nur noch eine Zeile verschlüsselten Text (ausgenommen natürlich, es ergibt sich durch Zufall ein Wagenrücklauf in der Verschlüsselung).
Nun wollen wir aus unseren beiden Ansatzpunkten ein Konzept aufbauen. Ich möchte nun das Konzept kurz wiedergeben und danach auf die einzelnen Punkte genauer eingehen, um die ganze Sache etwas verständlicher zu machen:
Der Text wird in einer Prozedur in einzelne Zeilen zerlegt. Diese Zeilen werden gesondert kodiert und am Ende wieder zusammengesetzt. Die Kodierprozedur wird wie folgt ablaufen: in einer for-Schleife wird der String (die Zeile) zeichenweise kodiert; sprich: das Muster (in unserem Fall: 2, 2, 0, 5, 1, 9, 8, 6) wird zum String addiert. Diese Vorgehensweise wurde bereits weiter oben besprochen, noch nicht erwähnt wurde allerdings, wie die programmtechnische Umsetzung dazu aussieht.
Das ist zwar nicht schwierig, aber ich möchte trotzdem einen kleinen Exkurs wagen: in unserem Beispiel wollen wir den String Zeichen für Zeichen kodieren - das heißt, dass jedes Zeichen einen Index (eine Position) im String hat. Das Zeichen "o" im Wort "Hallo" hat beispielsweise den Index 5. Wenn man nun ein einfaches Muster wie 3-2-1 verwendet, so wird jeder (nach einer mehr oder weniger kurzen Überlegungszeit) erkennen, dass das fünfte Zeichen im String "Hallo" mit 2 addiert werden muss, die zweite Position in unserem Muster. Im Kopf sieht das ganze überschlagsmäßig so aus: wenn du die Zeichenposition (5) durch die Musterlänge 3 dividierst, erhältst du (wie in der Volksschule) 1 mal, 2 Rest. Dieses "2 Rest" entspricht (zufälligerweise) genau unserem Ergebnis, nämlich 2.
In Delphi (auch in anderen Programmiersprachen) gibt es einen Operanden, der nur den Rest einer Division zurückgibt. Dieser Operand heißt mod. Ein Beispiel: 15 dividiert durch 4 ergibt 3 mal und 3 Rest. 15 mod 4 ist also 3.
Diesen kleinen Exkurs habe ich rein verständnishalber gemacht, denn wenn dieser kleine Teil nicht klar ist, macht die ganze Verschlüsselung Probleme.

Nun aber weiter mit unserem Konzept: der erste Teil besteht aus der Zerlegung des gesamten Memo-Textes. Das ist über eine for-Schleife am einfachsten möglich:

Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure Zeilen_einzeln_kodieren;
var
  i: Integer;
begin
  for i := 0 to Memo1.Lines.Count - 1 do begin
    Memo1.Lines[i] := Vinegre_Chiffre(Memo1.Lines[i]);
end;

Das wäre die kompakteste Schreibweise. Die Funktion "Vinegre_Chiffre" werden wir gleich im Detail besprechen; für diesen Ansatz reicht der Name der Funktion. Auch ein alter Bekannter kreuzt wieder unseren Weg: der List-Index 0. Die Zählung der Zeilen beginnt mit 0 und endet mit Zeilenzahl - 1. Aber das würde jetzt wirklich zu weit ausschweifen.
In dieser Prozedur wird natürlich vorausgesetzt, dass Memo1 existiert. Sollte Memo1 nicht existieren, sondern ein beliebiges Memofeld übergeben werden können, müsste man im Prinzip nur einen Parameter einbauen:

Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure Zeilen_einzeln_kodieren(Memo: TMemo);
var
  i: Integer;
begin
  for i := 0 to Memo.Lines.Count - 1 do begin
    Memo.Lines[i] := Vinegre_Chiffre(Memo.Lines[i]);
end;

Nun zur eigentlichen Chiffrierung: zu allererst wollen wir ein Array deklarieren, das unsere altbekannten Nummern enthält:

Quelltext
1:
2:
const
  verschiebungen: Array [1..8] of Byte = (2, 2, 0, 5, 1, 9, 8, 6);

Natürlich kannst du auch andere Ziffern verwenden (oder eine größere Zahlenvielfalt), aber ich denke, dass ein Beispiel zu Ende der Erklärung nicht schlecht wäre. Und dafür sind allzu große Arrays nicht geeigent (man verliert leicht die Übersicht).
Nun haben wir also bereits unser Array; was noch fehlt ist die eigentliche Verschlüsselung. Da wir - wie bereits erwähnt - zeichenweise kodieren, muss der String in Einzelteile zerlegt werden. Am Besten kann das über eine for-Schleife gemacht werden.
im nun folgenden Beispiel möchte ich die Caesar-Chiffre zeigen. Das weicht zwar ein klein wenig vom Thema ab, sollte aber für das Verständnis der Vinègre-Chiffre nicht von Nachteil sein. Der Code sei fürs Erste einmal in den Raum gestellt:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
function Caesar(s: String): String;
var
  i: Integer;
  dummy: String;
begin
  dummy := s;
  for i := 1 to Length(s) do
    dummy[i] := Chr(Ord(dummy[i]) + 3);
  Result := dummy;
end;

Das "3" soll hier nicht verwirrend wirken: wir nehmen einfach einmal an, dass wir in dieser Caesar-Verschlüsselung eine Verschiebung von 3 wählen.
Um nun genauer auf den Ablauf der Prozedur einzugehen: die Variable dummy dient als Unterstützung, um nicht auf die Original-Variable zugreifen zu müssen (s). Das ist vor allem bei DLLs wichtig - aber das soll hier nicht erwähnt werden. In der for-Schleife wird nun der i-ten Position der Variable dummy der Wert Chr(Ord(dummy[i]) + 3) zugewiesen. Da dieser Ausdruck etwas kompliziert ist, wollen wir ihn von innen nach außen auflösen:
dummy[i] ist das Zeichen an der aktuellen Position (i). Vorüberlegung: wie können wir zu diesem Zeichen 3 hinzuzählen? Ord und Chr sind die Funktionen, die uns dabei helfen können. Ord liefert die Position eines Zeichens in der ASCII-Tabelle. Das können wir hier anwenden: Ord(dummy[i]) liefert die Position des Zeichens an der aktuellen Position in der ASCII-Tabelle. Nun zählen wir 3 hinzu (+ 3) und verwandeln das Ganze wieder zurück in ein Zeichen, das dem Zeichen an der Position Ord(dummy[i]) + 3 entspricht.
Der Befehl Chr(Ord(dummy[i]) + 3) liefert also unser verschlüsseltes Zeichen. Das müssen wir nur noch zuweisen. Und wohin? Klar: dorthin, wo wir es hergenommen haben (dummy[i]): das i-te Zeichen unseres Strings.
Am Ende wird nur noch als Ergebnis unserer Prozedur dummy zurückgegeben (Result := dummy).
Nun haben wir die Caesar-Chiffre (hoffentlich) verstanden und können gleich direkt zur Vinègre-Chiffre übergehen: im Prinzip müssen wir nur einen Teil austauschen: da wir mit variabler Verscheibung arbeiten, müssen wir unser " + 3" durch "verschiebung[i mod 8]" ersetzen. Der Grunf wurde vorhin bereits besprochen: Position mod Musterlänge ergibt die Position des Musters, die addiert werden soll. Nun lautet unser Code:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
function Vinegre_Chiffre(s: String): String;
var
  i: Integer;
  dummy: String;
begin
  dummy := s;
  for i := 1 to Length(s) do
    dummy[i] := Chr(Ord(dummy[i]) + verschiebung[i mod 8]);
  Result := dummy;
end;

Damit hätten wir die Vinègre-Chiffre komplett aufgelöst und können uns noch einigen Feinheiten widmen: im Prinzip könnten wir auch in die Funktion selbst das Muster einbauen. Ein zusätzlicher Parameter wie zum Beispiel "Muster: Array of Byte" könnte uns die Deklaration des Konstanten-Arrays ersparen.
Um nun einen kompletten Überblick über die Verschlüsselung gewinnen zu können, werde ich die "ganze" Unit einblenden:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
Unit Vinegre;

interface

uses StdCtrls, SysUtils;

function Vinegre_Chiffre(s: String): String;
procedure Zeilen_einzeln_kodieren(Memo: TMemo);

implementation

function Vinegre_Chiffre(s: String): String;
var
  i: Integer;
  dummy: String;
begin
  dummy := s;
  for i := 1 to Length(s) do
    dummy[i] := Chr(Ord(dummy[i]) + verschiebung[i mod 8]);
  Result := dummy;
end;

procedure Zeilen_einzeln_kodieren(Memo: TMemo);
var
  i: Integer;
begin
  for i := 0 to Memo.Lines.Count - 1 do begin
    Memo.Lines[i] := Vinegre_Chiffre(Memo.Lines[i]);
end;

end.

Achtung: dieser Code setzt voraus, dass diese Unit separat vom anderen Programm erstellt und dann eingebunden wird. Das Hauptprogramm könnte dann beispielsweise so lauten:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
Unit Unit1;

interface

uses Windows, SysUtils, Graphics, Forms, Dialogs, Controls, Messages, StdCtrls {...}, Vinegre {nicht vergessen!!!};

type
  TForm1 = class (TForm)
    Button1: TButton;
    Memo1: TMemo;
  public
    procedure Button1Click(Sender: TObject);
    end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Zeilen_einzeln_kodieren(Memo1);
end;

end.

Damit wäre die Verschlüsselung vollständig. Der letzte Aufruf ("Zeilen_einzeln_kodieren(Memo1)") dürfte klar sein: der Inhalt des Memos wird kodiert. Übergabeparameter ist hierbei das Memo1 von Form1.
Was nun noch "fehlt" ist eine Entschlüsselungsprozedur. Die könnte man sich zwar eigentlich schenken, aber sie soll nicht unerwähnt bleiben: man muss nur das "+" in der Funktion "Vinegre_Chiffre" durch ein "-" ersetzen.
Man könnte allerdings auch in der Funktion selbst durch einen Parameter diese Funktionalität hervorrufen. Damit könnte man sich dann etwas Zeitaufwand sparen. Hier der Vollständigkeit halber die Implementation:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
function Vinegre_Chiffre(s: String; Encode: Boolean): String;
var
  i: Integer;
  dummy: String;
begin
  dummy := s;
  for i := 1 to Length(s) do begin
    if Encode then
      dummy[i] := Chr(Ord(dummy[i]) + verschiebung[i mod 8])
    else
      dummy[i] := Chr(Ord(dummy[i]) - verschiebung[i mod 8])
    end;
  Result := dummy;
end;

Wenn man nun kodieren will, ruft man die Prozedur mit dem zweiten Parameter als true auf; will man dekodieren: false.

Abschließende Bemerkungen:
1.) Natürlich könnte man den Quelltext so umprogrammieren, dass man einen zweiten Parameter übergeben kann (ein Memo, in das die kodierte Fassung des Textes gespeichert wird).
2.) Die letzten 13000 Zeichen wurden ohne Zuhilfenahme von Delphi oder einem Delphi-Buch geschrieben. Dieser Text wurde rein aus dem Stehgreif geschrieben und besitzt keinen Anspruch auf Korrektheit. Möglicherweise besitzen sogar die Prozeduren und Funktionen an sich logische Fehler und können (rein kopiert) nicht in Delphi kompiliert werden.
In diesem Sinne möchte ich noch kurz etwas anhängen: sollte irgendjemand Fehler in diesem Text finden, ist er herzlich eingeladen, mir diese mitzuteilen. Aber ich denke, dass diese (guten) anderthalb Stunden Arbeit nicht allzu viele Fehler (mal abgesehen von Rechtschreib- und Grammatikfehlern) beherbergen dürften.

Ich hoffe, alle um ein wichtiges Kapitel des Verschlüsselung bereichert zu haben,
So long,
AXMD


Tino - Di 18.02.03 10:08

D0P3 F!$H hat folgendes geschrieben:
Auch wenn das hier schon wieder zu einem Privatthread wird und es übelst OffTopic ist

Kein Problem :evil: Der OffTopic Anteil wird bald gelöscht!

AXMD hat folgendes geschrieben:
Solltest du dennoch Fragen haben, kannst du mir entweder eine private Nachricht oder eine Email schreiben. Scheu dich nicht: jeder fängt mal an!

Bitte die weiteren Fragen und Antworten hier im Forum... so haben alle etwas davon!

Gruß
TINO


AXMD - Di 18.02.03 11:53

Oh, sorry,Tino. Sicher! Hab ich nur vergessen zu sagen :oops:

Außerdem: es heißt Viginère :oops: :oops: :oops: nicht Vinègre

AXMD


Tino - Di 18.02.03 12:12

AXMD hat folgendes geschrieben:
Oh, sorry,Tino. Sicher! Hab ich nur vergessen zu sagen :oops:

Kein Problem :-)

Gruß
TINO


Christian S. - Di 18.02.03 12:28

So, um Tino zu besänftigen, ein produktiver Beitrag:
Dein Code wird - glaube ich - beim achten Zeichen der Verschlüsselung einen Fehler ausgeben, denn Dein Array beginnt mit Index 1, aber 8 mod 8 ergibt 0. Außerdem kann es keinen Rest 8 geben, also muss Dein Array so aussehen:


Quelltext
1:
2:
const 
  verschiebungen: Array [0..7] of Byte = (2, 2, 0, 5, 1, 9, 8, 6);


Sonst habe ich beim Drüberlesen keine Fehler mehr gefunden.

MfG,
Peter