Strona główna » Algorytmy » Szyfry » Szyfr Portax
 

Szyfr Portax

Szyfr

Szyfr Portax to szyfr, który powstał na bazie szyfru Porta. Zabezpieczanie danych odbywa się poprzez szyfrowanie kolejnych par znaków tekstu jawnego. Do szyfrowania tekstu jawnego służy tabelka, która składa się z dwóch alfabetów:

A1/1ABCDEFGHIJKLM
A1/2NOPQRSTUVWXYZ
A2ACEGIKMOQSUWY
BDFHJLNPRTVXZ

Jak można zauważyć wiersz A1/1 jest utworzony z liter od A - M. Bardzo ważne, aby pamiętać, że jest to wiersz, który należy odpowiednio przesunąć dla aktualnego znaku z klucza. Litera "A" z A1/1 musi być ustawiona w kolumnie w której znak klucza znajduje się w alfabecie A2. Drugi wiersz i zarazem druga część pierwszego alfabetu A1/2 to pozostałe litery N - Z. Alfabet drugi w pierwszym wierszu zawiera litery o pozycjach nieparzystych, a w drugim te o indeksach parzystych.

Do szyfrowania potrzebny jest jeszcze klucz. Kluczem może być dowolne słowo, ale najlepiej jeśli nie jest zbyt długie, a litery się nie powtarzają. Długość klucza ma znaczenie na grupowanie znaków. Podczas szyfrowania należy zapisać kolejne znaki w tabeli o tylu kolumnach jakiej długości jest klucz. Następnie grupując wiersze po dwa należy szyfrować kolejne pary w każdej kolumnie. Tekst wynikowy powstaje poprzez zastąpienie znaków tekstów jawnego zaszyfrowanym i odczytanie go wierszami. Jeśli tekst jest długości nieparzystej to ostatni znak przepisuje się bez zmiany, albo dopisuje się jakiś losowy znak do tekstu jawnego.

Szyfrowanie pary polega na znalezieniu pierwszego znaku w pierwszym alfabecie (wiersz 1, 2), a drugiego znaku w drugim alfabecie (wiersz 3, 4). Jeśli wyszukiwane znaki znajdują się w tej samej kolumnie to każdy znak przechodzi na znak w tej samej kolumnie, ale z drugiego wiersza w obrębie tego samego alfabetu. W przypadku, gdy wyszukiwane znaki nie są w tej samej kolumnie to traktujemy je tak, że wyznaczają dwa rogi prostokąta. Szukamy pozostałych dwóch wierzchołków i pod nimi kryją się zaszyfrowane litery. Przepisujemy najpierw znak z alfabetu A1, a potem A2.

Proces deszyfrowania danych polega na ponownym zaszyfrowaniu danych przy pomocy tego samego klucza.

Przykład

Weźmy przykładowo klucz "HASLO" i tekst "NIEZWYKLETAJNAINFORMACJA". Pierwszy etap polega na zbudowaniu tabeli do szyfrowania. Nad tabelką został zapisany klucz, aby było wiadomo, którą parę jakim znakiem klucza szyfrujemy:

HASLO
NIEZW
YKLET
AJNAI
NFORM
AC
JA

Podczas tworzenia takiej tabeli należy pamiętać, że dążymy do utworzenia jak największej ilości par. Spróbujmy teraz zaszyfrować parę znaków NY. W tym celu należy rozpisać tabelkę szyfrującą dla znaku szyfrującego 'H', a następnie znaleźć znaki 'N' i 'Y' odpowiednio w A1 i A2.

A1/1JKLABCDEFGHIJ
A1/2NOPQRSTUVWXYZ
A2ACEGIKMOQSUWY
BDFHJLNPRTVXZ

Znaki nie są w tym samym wierszu, więc szukamy prostokąta. Na czerwono znaleziono znaki N i Y. Z kolei na zielona znaleziono parę szyfrującą NY - jest to para ZA.

Spróbujmy teraz zaszyfrować parę IK. Wymaga to zmiany tabelki szyfrującej, ponieważ teraz nie szyfrujemy literą 'H' tylko 'A'. Szukamy znaków:

A1/1ABCDEFGHIJKLM
A1/2NOPQRSTUVWXYZ
A2ACEGIKMOQSUWY
BDFHJLNPRTVXZ

Znaleziona para szyfrująca IK to FQ. Rozpatrzmy jeszcze parę WT szyfrowaną znakiem 'O'.

A1/1GHIJKLMABCDEF
A1/2NOPQRSTUVWXYZ
A2ACEGIKMOQSUWY
BDFHJLNPRTVXZ

Znalezione znaki są z tej samej kolumny, więc szyfrowanie polega na zamienie numera wiersza w obrębie tego samego alfabetu. W tym przypadku WT przechodzi na CS. Ostateczna tabelka, która pokazuje kolejno zaszyfrowane znaki przedstawia się następująco:

KluczHASLOHASLOHA
Znak1NIEZWAJNAIAC
Znak2YKLETNFORMJA
SzyfrogramZAFQJBPYCSDHCTUADLMEBHAE

Ostateczny szyfrogram to: ZFJPCAQBYSDCUDMHTALEBAHE. W celu odszyfrowania danych należy ponownie zaszyfrować szyfrogram przy pomocy tego samego klucza.

Implementacja

Poniższa implementacja zmienia dane zgodnie z szyfrem Portax. Zakłada ona poprawność wprowadzonych danych. Tekst jawny jak i hasło powinny być zapisane wielkimi litera i nie powinno być innych znaków.

Typy zmiennych

W celu uproszczenia kodu zostały zdefiniowane dwa typy danych: pozycja do przechowywania pozycji znaku oraz para do przekazywania par znaków.

  1. typedef pair<int, int> pozycja;
  2. typedef pair<char, char> para;

Podawanie pozycji znaku

W programie nie jest przechowywana tabelka, ponieważ jest ona dynamiczna, a poza tym każdy z wierszy bardzo łatwo opisać przy pomocy jednolinikowej funkcji. Funkcja podajPozycje() dla określonego alfabetu tablica, znaku szyfrowanego znak szyfrowanego znakiem znak_klucza zwraca pozycję znaku w alfabecie. Pierwszy pozycja to numer wiersza w tabeli (indeksowanie od 1), a drugi to indeks kolumny (indeksowanie od 0).

  1. pozycja podajPozycje(int tablica, char znak, char znak_klucz) {
  2.   if (tablica == 1) {
  3.     if (znak < 'N') {
  4.       return make_pair(1, ((znak - 'A') + (znak_klucz - 'A') / 2 + 13) % 13);
  5.     } else {
  6.       return make_pair(2, znak - 'N');
  7.     }
  8.   } else {
  9.     if ((znak - 'A') % 2 == 0) {
  10.       return make_pair(3, (znak - 'A') / 2);
  11.     } else {
  12.       return make_pair(4, (znak - 'B') / 2);
  13.     }
  14.   }
  15. }

Znak na pozycji

Funkcja znakPozycja() pozwala skonwertować punkt na konkretny znak. W tym celu wystarczy podać konkretną pozycję (numer wiersza w tabeli oraz indeks kolumny) punkt oraz znak szyfrujący znak_klucz. Funkcja zwraca znak znajdujący się w tabeli na danej pozycji.

  1. char znakPozycja(pozycja punkt, char znak_klucz) {
  2.   switch (punkt.first) {
  3.     case 1:
  4.       return 'A' + (punkt.second - (znak_klucz - 'A') / 2 + 13) % 13;
  5.     case 2:
  6.       return 'N' + punkt.second;
  7.     case 3:
  8.       return 'A' + punkt.second*2;
  9.     case 4:
  10.       return 'B' + punkt.second * 2;
  11.   }
  12. }

Szyfrowanie pary

Do zaszyfrowanie pary służy funkcja szyfrujPare(), która przyjmuje dwa argumenty: znaki - para znaków do zaszyfrowania oraz znak_klucz - znak szyfrujący daną parę.

  1. para szyfrujPare(para znaki, char znak_klucz) {
  2.   pozycja punkt1 = podajPozycje(1, znaki.first, znak_klucz);
  3.   pozycja punkt2 = podajPozycje(2, znaki.second, znak_klucz);
  4.   pozycja a, b;
  5.   if (punkt1.second == punkt2.second) {
  6.     a = make_pair(punkt1.first % 2 + 1, punkt1.second);
  7.     b = make_pair((punkt2.first - 2) % 2 + 3, punkt2.second);
  8.   } else {
  9.     a = make_pair(punkt1.first, punkt2.second);
  10.     b = make_pair(punkt2.first, punkt1.second);
  11.   }
  12.   return make_pair(znakPozycja(a, znak_klucz), znakPozycja(b, znak_klucz));
  13. }

(2. - 3.) Określ pozycję znaków. (5.) Jeśli są w tej samej kolumnie to: (6. - 7.) zamień indeksy wierszy, w przeciwnym razie (9. - 10.) zamień jedynie indeksy kolumn (najprostszy sposób znajdowania pozostałych dwóch wierzchołków prostokąta). Na koniec (12.) zwróć parę znaków na pozycjach kolejna a i b.

Szyfrowanie

Do szyfrowania podanego tekstu tekst przy poomcy klucza klucz służy funkcja szyfruj().

  1. char* szyfruj(char* tekst, char* klucz) {
  2.   int dl_klucz = strlen(klucz);
  3.   int dl = strlen(tekst);
  4.   char* wynik = new char[dl + 1];
  5.   int i = 0;
  6.   int dl_grupa;
  7.   do {
  8.     int t = (dl - i) / 2;
  9.     dl_grupa = t > dl_klucz ? dl_klucz : t;
  10.     for (int j = 0; j < dl_grupa; j++) {
  11.       para znaki = make_pair(tekst[i + j], tekst[i + dl_grupa + j]);
  12.       para p = szyfrujPare(znaki, klucz[(i + j) % dl_klucz]);
  13.       wynik[i + j] = p.first;
  14.       wynik[i + dl_grupa + j] = p.second;
  15.     }
  16.     i += dl_grupa * 2;
  17.   } while (dl_grupa != 0);
  18.   for (; i <= dl + 1; i++)
  19.     wynik[i] = tekst[i];
  20.   return wynik;
  21. }

Oblicz (2.) długość klucza i (3.) tekstu jawnego. (4.) Utwórz nową tablice pod wynik i zadeklaruj (5.) indeks przesuwania oraz jaką długość ma grupa. (6.) W pętli (7.) wylicz ile znaków zostało do końca i na tej podstawie (8.) określ ile par znajduje się w kolejnych dwóch wierszach. (9. - 14.) dla każdej pary znajdź ich zaszyfrowane postacie. Potem (16.) przesuń aktualny indeks o podwójną długość grupy. (17.) Warunkiem wykonywania pętli jest istnienie chociaż jednej pary w tekście. (18. - 19.) Jeśli liczba znaków jest nieparzyste to przepisz ostatni znak bez zmian. Na koniec (20.) zwróć wskaźnik na tablicę wynik.

Testowanie funkcji

W celu przetestowania programu można skorzystać z poniższego kodu:

  1. int main () {
  2.   char* klucz = new char[32];
  3.   cout << "Podaj klucz: ";
  4.   cin.getline(klucz, 32);
  5.   char* tekst = new char[1024];
  6.   cout << "Podaj tekst jawny:\n";
  7.   cin.getline(tekst, 1024);
  8.   char* szyfrogram = szyfruj(tekst, klucz);
  9.   cout << "\nSzyfrogram:\n";
  10.   cout << szyfrogram << endl;
  11.   char* tekstrozszyfrowany = szyfruj(szyfrogram, klucz);
  12.   cout << "\nTekst rozszyfrowany:\n";
  13.   cout << tekstrozszyfrowany << endl;
  14.   delete klucz, tekst, szyfrogram;
  15.   system("pause");
  16.   return 0;
  17. }

Zadania

Zadanie 1

Napisz wersję program Portax, który będzie mógł przyjmować dowolny tekst, a szyfrowane będą jedynie znaki alfabetu łacińskiego. Dodatkowo po zakończeniu szyfrowani i-ty znak tekstu jawnego powinien mieć tą samą wielkość co przed szyfrowanie. Przykładowo tekst "Poufne dane zaszyfrowane szyfrem Portax." szyfrowany kluczem "HASLO" powinien dać w wyniku:

  1. Mbnbpf cova piuynzbkywpi ptubjky Xwndix.