Szyfrowanie Homofoniczne

Ccobras

<font size="4">1.Czym jest szyfrowanie Homofoniczne?</span>


-Szyfr homofoniczny podstawieniowy jest szyfrem polegającym na tym, że każdemu pojedynczemu znakowi tekstu jawnego zostaje przyporządkowane kilka znaków. Przyporządkowane każdej literze zestawy znaków nazywamy homofonami.

W naszym przypadku każdemu znakowi będziemy przyporządkowywać 3 kody 3-znakowe.
Dla przykładu:
Alfabet
1 A
2 B
3 C
4 D
Kody
1 'sdf' 'aaa' 'gfg'
2 'xxx' 'bvb' 'cvb'
3 'aqw' '34g' '545'
4 'hgf' '343' '545'
Każdy kod musi być unikatowy!

<font size="4">2. Delphi<font size="4"></span></span>


Jak to będzie działać?
-Sprawa jest bardzo prosta:) program będzie przeszukiwał Memo i jeśli napotka któryś ze znaków alfabetu wylosuje odpowiadający mu kod (1 z 3) z Tablicy Kody .

<font size="4">3. Zaczynamy</span>


Na wstępie należy zdać sobie sprawę z tego ile różnych znaków musimy zakodować. Przecież występują małe litery, duże, cyfry, znaki specjalne. Po podliczeniu okaże się że liczba znaków jest spora - około 100. Teraz Każdemu znakowi przyporządkowujemy 3 kody 3-cyfrowe i ze 100 robi się 300 pozycji. Trzymanie tych informacji w naszym programie byłoby uciążliwe. Skorzystamy więc z biblioteki dll i plików *.res.

Tworzymy pliki rc i res
Otwórzmy notatnik i wpiszmy w nim:

STRINGTABLE
BEGIN
  1, "A"
  2, "Ą"
  3, "a"
  4, "ą"
  5, "B"
  6, "b"
END

Oczywiście należy powyższy kod uzupełnić o inne znaki. W załączniku znajduję się plik w którym zawarłem 94znaki (małe litery, duże, polskie znaki, i znaki specjalne). Nazwijmy ten plik Alfabet.rc Teraz musimy plik Alfabet.rc zamienić na Alfabet res. Posłuży nam do tego aplikacja Borlanda (Borland\Delphi\Bin\brcc32.exe). Skopiujmy ten program do folderu w którym znajduję się plik rc.
W oknie konsoli (Starty->Uruchom->CMD) przejdźmy do naszego folderu i wpiszmy:

brcc32.exe Alfabet.rc

Tym sposobem pojawił się w tym folderze plik Alfabet.res
Teraz powinniśmy zająć się Listą Kodów. Musimy powtórzyć całą operację od początku, a więc otwórzmy notatnik i wpiszmy:

STRINGTABLE
BEGIN
  1, "1jh"
  2, "2df"
  3, "3hg"
  4, "4ti"
  5, "5uy"
  6, "6er"
  7, "7gf"
  8, "8df"
  9, "9de"
END

Tak jak napisałem wyżej każdemu znakowi z Alfabetu odpowiadają 3 kody. Ja dla przykładu stworzyłem tu 9 RÓŻNYCH kodów, aczkolwiek powinno ich być 3*94=282 (pełna tabela kodów w załączniku). Zapiszmy ten plik jako Lista.rc. Postępując analogicznie przekształćmy go na Lista.res.

Tworzymy własną bibliotekę dll
Jak się domyślasz musimy stworzyć dwie biblioteki jedną dla alfabetu i drugą dla kodów.
Project->New->Other->Dll Wizard
Ukazało się nam okno w którym możemy zacząć pisać swoją bibliotekę. W naszym przypadku kod będzie króciutki. Przecież chodzi tylko o załadowanie pliku res.

  • Dla Alfabet.res
library AlfabetDLL;

{$R Alfabet.RES}

begin
end.

Zapiszmy ten projekt pod nazwą AlfabetDLL. Biblioteki Dll mają to do siebie że nie możemy ich uruchomić. Więc nie próbuj wciskać zielonego trójkąta Run:) Ale musimy je skompilować, więc Project->Compile AlfabetDll w tym momencie w naszym folderze powinna ukazać się biblioteka AlfabetDLL.dll

  • Dla Lista.res
library ListaKodowDL;

{$R Lista.RES}

begin
end.

Przeprowadźmy kompilację i tyle jeśli chodzi o tworzenie biblioteki :)

<font size="4">4.Właściwy Program<font size="4"></span></span>


Umieśćmy na formie 3 memo, oraz dwa przyciski. W pierwszym Memo będziemy wpisywać tekst, w drugim zobaczymy go w postaci zakodowanej, zaś w trzecim odkodowany. Pierwszym przyciskiem będziemy kodować tekst, drugim jak się pewnie domyślasz odkodujemy go.
Zmienne które wykorzystuje dodaj do sekcji var:

Var
  Form1                  : TForm1;
  I, J, K, Alf, Losowana : Integer;
  Tekst, TrzyZnaki       : String;
  Alfabet                : Array[0..255] of String;
  ListaKodow             : Array[1..500, 1..3] of String;

Ładujemy nasze bilbioteki
Stwórzmy takie dwie procedury:

Procedure LadowanieAlfabetu;
var
  DLL    : THandle;  //Uchwyt do biblioteki DLL
  Buffer : Array[0..255] of Char;  // Bufor przechowujący tekst
  I      : Integer;   //Zmienna potrzebna do pętli
begin
 DLL := LoadLibrary('AlfabetDLL.dll');  //Załaduj bibliotekę Alfabetu
 Try
 //Załadowanie tekstu
 For I := 1 to 94 do
 Begin
  LoadString(DLL, i, Buffer, SizeOf(Buffer));
  //Do tablicy Alfabetu wczytuje dane z biblioteki
  Alfabet[i] := Buffer;
 End;
 Finally
  FreeLibrary(DLL);   //Zwalniam bibliotekę
 End;
End;

Procedure LadowanieKodow;
Var
  DLL    : THandle;  //Uchwyt do biblioteki DLL
  Buffer : Array[0..255] of Char; // Bufor przechowujący tekst
  I, W, Z: Integer;  //Potrzebne do pętli
Begin
 W:= 1;   //odnosi się do kolumn, na początku =1
 Z := 1;   //odnosi się do wierszy, na początku =1
 DLL := LoadLibrary('ListaKodowDL.dll');   //Ładuje bibliotekę Kodow
 Try
 //Załadowanie tekstu
 For I := 1 to 282 So   //bo tyle jest wierszy w bibliotece kodów
 Begin
  LoadString(DLL, i, Buffer, SizeOf(Buffer));
  //Te 282 wiersze z biblioteki trzeba podzielić na tablicę dwuwymiarową
  // 3 kolumny i 94 wiersze
  ListaKodow[w][z] := Buffer;
  Inc(Z);
  if Z = 4 Then
  Begin
   Inc(W);
   Z := 1;
  End;
 End;
 Finally
   FreeLibrary(DLL);   //Zwalniam Bibliotekę
 End;
End;

Wczytaliśmy już nasz Alfabet oraz Kody, teraz należy napisać procedury którymi zakodujemy tekst wpisany w Memo1. Zakodowany ukaże się w Memo2, a odkodowany w Memo3.
A więc, do dzieła !

Procedure Kodowanie;
Begin
 Form1.Memo2.Clear;  //Trzeba wyczyścić memo
 For I := 0 To Form1.Memo1.Lines.Count-1 Do   //Po wszystkich wierszach
 Begin
  For J := 1 to Length(Form1.Memo1.Lines[i]) Do // Po wszystkich elementach w wierszu
  Begin
   For Alf := 1 to 94 do //Po wszystkich wierszach Tablicy Alfabet
   Begin
    //Jeżeli konkretny element memo będzie taki sam jak kolejny element Alfabetu
    if Form1.Memo1.Lines[i][j] = Alfabet[alf] Then
    Begin
     //to wylosuj liczbę z przedziału 1-3
     Losowana := Random(3)+1; //Aby nie było 0
     //przypisz że zmienna tekst jest równa to co miała do tej pory
     //plus Kod odpowiadający danemu znakowi (alf) i wylosowana pozycja kodu
     Tekst := Tekst + ListaKodow[Alf][Losowana]; 
    End;
   End;    //koniec pętli po alfabecie  
  End;  //koniec pętli po elementach wiersza
  //Po przeanalizowaniu całego wiersza dodaje jedną linię do memo
  Form1.Memo2.Lines.Add(Tekst);   //dodaje do memo zmienna tekst
  Tekst := '';     //czyszcze tekst dla nowej petli 
 End;        //Koniec drugiej petli
End;

Procedure Dekodowanie;
Var Kol, Wier : Integer;
Begin
 Form1.Memo3.Lines.Clear;   //czyszczę memo
 For I := 0 To Form1.Memo2.Lines.Count-1 Do  //Po wszystkich wierszach
 Begin
  For J := 1 to Length(Form1.Memo2.Lines[i]) do //Po wszystkich elementach wiersza
  Begin
   // Ładuje każdy wiersz po 3 znaki (bo kod jest 3-znakowy)
   TrzyZnaki := TrzyZnaki + Form1.Memo2.Lines[i][j];
   //Jeżeli wczytałem 3 znaki (k = zero na początku)
   if J = K+3 Then
   Begin
    For Kol := 1 To 94 Do   //Po wszystkich wierszach kodów
    Begin
     For Wier := 1 To 3 Do    //Po wszystkich elementach każdego wiersza kodów
     Begin
      //Jeżeli wczytane do tej pory trzyznaki pasują do któregoś z kodów...
      if TrzyZnaki = ListaKodow[Kol][Wier] Then
      Begin
       //...To do zmiennej tekst dopisuje konkretny znak z alfabetu
       Tekst := Tekst + Alfabet[Kol];
      End; 
     End;
    End; 
    Inc(K, 3);    //Zwiększ k o 3 żeby sprawdzić kolejne 3 znaki w tym wierszu
    TrzyZnaki := '';  //Wyczyść trzy znaki by móc wczytać nowe, to co wyżej 
   End;
   //Jeżeli k będzie równe długości wiesza wówczas kończę operację dla tego wiersza
   if K = Length(Form1.Memo2.Lines[i]) Then
   Begin
    K := 0; //Ustawiam z powrotem k na zero
   End;
  End;  //Koniec pętli po elementach wiersza
  Form1.Memo3.Lines.Add(tekst);   //dodaje do memo zmienna tekst
  Tekst := '';     //czyszcze tekst dla nowej petli
 End;  //Koniec pętli po ilości wierszy
End;

To już prawie koniec, teraz tylko wpiszmy W Buttonie1 (który będzie kodował)

Procedure TForm1.Button1Click(Sender: TObject);
Begin
 LadowanieAlfabetu;
 LadowanieKodow;
 Kodowanie;
End;

Oraz dla Buttona2:

Procedure TForm1.Button2Click(Sender: TObject);
Begin
 Dekodowanie;
End;

To już naprawdę koniec. Zdaję sobie sprawę że z formatowaniem tego tekstu szału nie ma :) Ale jest to pierwszy mój artykuł więc proszę o wyrozumiałość.
Podsumujmy
Program wczytuje sobie znaki (małe litery,duże,polskie znaki,specjalne,cyfry) do tablicy -94kolumn i 1 wiersz, oraz tworzy tablice również o 94 wierszach (każdy wiersz kodu odpowiada jednemu znakowi) i 3 kolumnach ponieważ dla danego znaku mogą być 3 różne kody. Następnie sprawdza tekst wpisany w Memo1 porównując go z tablicą znaków. Jeśli wpisany znak odpowiada w tablicy np numerowi 44 wówczas z tablicy kodów losuje hasło [44][1] [44][2] lub [44][3]. Dekodowanie sprawdza czy kod wpisany występuje w tablicy kodów. Gdy znajduje się np na pozycji [44][3] wówczas wprowadza odkodowany znak z alfabetu który znajduje się na pozycji 44.


<font size="3">Autor: Tomasz Bartnik</span><right></right>

Aplikacja:
Szyfrow_Homofoniczne.rar

4 komentarzy

Jak chcesz to popraw
Miałem mało czasu, zaraz mieliśmy z rodzinką jechać do Parku Miniatur :)

Patryk: wcięcie na poziomie jednej spacji? Nie sądzę ;)

Szkoda, że autor nie napisał tego tydzień temu.
Jakiś użytkownik czegoś takiego szukał :-)

Poprawiłem formatowanie kodu, teraz jest czytelny

alfabety można łatwo zdefiniować wewnątrz programu - tablica tablic stringów inicjowana wartościami inline - od razu w deklaracji.

opisana metoda jest fajniejsza od zwykłego szyfru podstawieniowego i tylko minimalnie bardziej skomplikowana, ale przy odpowiednio długim tekście tak samo narażona na złamanie przez analizę statystyczną. żeby była w miarę skuteczna, tekst nie może być długi, alfabet z podstawieniami długi (kilkadziesiąt alternatyw dla każdej litery), ilość podstawień dla każdej litery powinna być inna (najwięcej dla najczęściej używanych liter - to powinno mocno utrudnić analizę statystyczną), a długość "liter" alfabetu podstawionego powinna być zmienna (ale oczywiście jednoznaczna).
do szybkiego odszyfrowania przydałaby się tablica haszująca.

co do samego srtykułu - fatalnie formatujesz kod. po każdym begin, type, var, try itp powinno być wcięcie bloku kodu.