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

Szyfr Nicodemus

Wstęp

Szyfr Nicodemus łączy w procesie szyfrowania danych zarówno transpozycje jak i podstawienie. Jego zaletą jest możliwość wykorzystania dowolnego szyfru podstawieniowego w celu zaszyfrowania danych. Sugerowanymi szyframi podstawieniowymi są Szyfr Vigenère oraz Szyfr Porta. Jego wadą jest fakt, że korzystając z szyfrów podstawieniowych nie jest on odporny na ataki, które wykorzystują częstotliwość występowania liter.

Opis

Klucz

W celu zaszyfrowania danych szyfrem Nicodemus należy określić słowo klucz, które powinno składać się z unikalnych, pięciu liter. Na podstawie klucza należy utworzyć klucz liczbowy. Metoda polega na przyporządkowaniu każdej literze pozycję na której się znajduje po ustawieniu liter klucza w sposób alfabetycznie.

Przykładowo dla klucza "HASLO" otrzymamy 21534, ponieważ po posortowaniu alfabetycznie HASLO otrzymamy AHLOS.

Szyfrowanie danych

Pierwszy etap szyfrowania danych polega na zapisaniu tekstu jawnego w wiersze po pięć znaków. Z kolei w pierszym wierszu, w nagłówku, należy zapisać otrzymany klucz liczbowy. Przykładowo dla "TAJNA INFORMACJA" otrzymujemy:

21534
TAJNA
INFOR
MACJA

Gdyby w wybranym tekście jawnym długość tekstu jawnego nie byłaby podzielna przez pięć to należy dopisać na koniec np. odpowiednią ilość znaków przerwy, albo lepiej losowych znaków. Kolejny krok polega teraz na przestawieniu liter tak, aby w pierwszym wierszu liczby były posortowane rosnąco.

12345
ATNAJ
NIORF
AMJAC

Z tego można odczytać tymczasowy szyfrogram: ATNAJNIORFAMJAC, który następnie należy zaszyfrować dowolnym szyfrem podstawieniowym przy pomocy klucza podanego w formie znaków. Używając szyfru Vigenère ostatecznym szyfrogramem jest HTFLXUIGCTHMBLQ.

Warto zauważyć, że etap transpozycji i podstawienia nie są procesami oddzielnymi i trzeba zachowywać ich kolejności.

Implementacja

Przedstawiony przykładowy program zakłada, że wpisywane dane składają się jedynie z dużych liter alfabet łacińskiego. Nie jest sprawdzana poprawność wprowadzonych danych. Podany program działa poprawnie tylko, gdy długość tekstu jawnego jest wielokrotnością 5.

Tworzenie klucza

Utworzenie klucza liczbowego polega na uporządkowaniu liter klucza. Jednak przed sortowaniem należy do każdej wartości dołączyć jej pozycję początkową. Następnie po posortowaniu otrzymujemy pewne wartości, które wskazują gdzie dana litera była na początku. W tym momencie wystarczy, aby wartość z i-tej pozycji miała wartość i.

  1. int* utworzKlucz(char* klucz) {
  2.   char* kluczKopia = new char[5];
  3.   for (int i = 0; i < 5; i++)
  4.     kluczKopia[i] = klucz[i];
  5.   int* kluczL = new int[5];
  6.   for (int i = 0; i < 5; i++)
  7.     kluczL[i] = i;
  8.   for (int x = 0; x < 5; x++) {
  9.     for (int y = x; y < 5; y++) {
  10.       if (kluczKopia[x] > kluczKopia[y]) {
  11.         swap(kluczKopia[x], kluczKopia[y]);
  12.         swap(kluczL[x], kluczL[y]);
  13.       }
  14.     }
  15.   }
  16.   int* kluczLiczby = new int[5];
  17.   for (int i = 0; i < 5; i++)
  18.     kluczLiczby[kluczL[i]] = i;
  19.   delete kluczKopia, kluczL;
  20.   return kluczLiczby;
  21. }

Najpierw (2. - 4.) utwórz kopię zmiennej klucz, ponieważ podczas sortowania dane zawarte w tej zmiennej uległyby zmianie. (5.) Utwórz tymczasową listę danych i (6. - 7.) przypisz każdej pozycji wartość pozycji. (8. - 15.) Używając sortowania bąbelkowego posortuj skopiowany klucz powiązany z utworzoną tymczasową listą danych. Po posortowaniu pozostaje (16. - 18.) utworzyć gdzie pozycja równa i-tej wartości na liście danych jest równa i. Przed (20.) zwróceniem wyniku należy (19.) usunąć tymczasowe zmienne.

Szyfrowanie

Poniższa funkcja szyfruj() szyfruje dane zgodnie z podanym kluczem oraz Szyfrem Vigenère.

  1. char* szyfruj(char* txt, char* klucz) {
  2.   int* kluczL = utworzKlucz(klucz);
  3.   int dl = strlen(txt);
  4.   char* wynik = new char[dl + 1];
  5.   for (int i = 0; i < dl; i++) {
  6.     char c = ((txt[i] - 'A') + (klucz[kluczL[i % 5]] - 'A')) % 26 + 'A';
  7.     wynik[(i / 5) * 5 + kluczL[i % 5]] = c;
  8.   }
  9.   wynik[dl] = '\0';
  10.   delete kluczL;
  11.   return wynik;
  12. }

Funkcja kolejno: (2.) generuje klucz liczbowy i (3.) sprawdza długość tekstu wejściowego. Następnie (4.) tworzy nową tablicę pod zapisanie wyniku. (5.) Dla każdej kolejne litery: (6.) wyliczana jest wartość podstawienia, a następnie (7.) jest zapisywana do zmiennej wynik w odpowiednie miejsce. Po zamianie wszystkich znaków (8.) dopisz znak końca danych, (9.) usuń wygenerowany klucz i (10.) zwróć wynik.

Deszyfrowanie

Poniższa funkcja rozszyfruj() deszyfruje dane zgodnie z podanym kluczem oraz Szyfrem Vigenère. W tym przypadku jednak klucz zamiany musi zostać dodatkowy zintepretowany, ponieważ szukamy przekształcenia odwrotnego do tego podanego w kluczu liczbowym.

  1. char* rozszyfruj(char* txt, char* klucz) {
  2.   int dl = strlen(txt);
  3.   char* wynik = new char[dl + 1];
  4.   int* kluczL = utworzKlucz(klucz);
  5.   for (int i = 0; i < dl; i++) {
  6.     char c = ((txt[i] - 'A') - (klucz[i % strlen(klucz)] - 'A') + 26) % 26 + 'A';
  7.     wynik[(i / 5) * 5 + szukaj(i % 5, kluczL, 5)] = c;
  8.   }
  9.   wynik[dl] = '\0';
  10.   delete kluczL;
  11.   return wynik;
  12. }

Funkcja kolejno: (2.) sprawdza długość tekstu wejściowego, (3.) tworzy nową tablicę pod zapisanie wyniku i (4.) generuje klucz liczbowy. (5.) Dla każdej kolejne litery: (6.) wyliczana jest wartość podstawienia, a następnie (7.) jest zapisywana do zmiennej wynik w odpowiednie miejsce. Tu warto zauważyć, że przekształcenie odwrotne polega na wyszukaniu w kluczu liczbowym kluczL aktualnej kolumny znaku. Po zamianie wszystkich znaków (8.) dopisz znak końca danych, (9.) usuń wygenerowany klucz i (10.) zwróć wynik.

Funkcja użyta do szukania danych nazywa się szukaj() i przyjmuje trzy argumenty: wartosc - szukana wartość, lista - lista danych oraz n - długość listy.

  1. int szukaj(int wartosc, int* lista, int n) {
  2.   for (int i = 0; i < n; i++)
  3.     if (lista[i] == wartosc)
  4.       return i;
  5.   return -1;
  6. }

Testowanie funkcji

Poprawność działania napisanych funkcji można sprawdzić poniższą funkcją main(), któa wczytuje od użytkownika tekst jawny oraz klucz.

  1. int main() {
  2.   cout << "Podaj tekst do zaszyfrowania:\n";
  3.   char* txt = new char[512];
  4.   cin.getline(txt, 512);
  5.   cout << "Podaj klucz:\n";
  6.   char* klucz = new char[6];
  7.   cin.getline(klucz, 6);
  8.   char* txtc = szyfruj(txt, klucz);
  9.   cout << "\nSzyfrogram:\n" << txtc << endl;
  10.   char* txtd = rozszyfruj(txtc, klucz);
  11.   cout << "\nTekst jawny:\n" << txtd;
  12.   delete[] txt, txtc, txtd, klucz;
  13.   system("pause");
  14.   return 0;
  15. }

Zadania

Zadanie 1

Napisz funkcję szyfruj() oraz rozszyfruj(), która szyfruje dane zgodnie z szyfrem Nicodemusa i jako szyfru podstawionego używa szyfru Porta.

Przykładowo dla tekstu 'TAJNAINFORMACJA' i klucz 'HASLO' poprawnym szyfrogram jest: 'QGESQKVFMZQZSSW'.