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

Szyfr VIC

Wstęp

Szyfr VIC został wymyślony w taki sposób, aby można go było używać bez wykorzystania sprzętu elektronicznego. Sposób szyfrowania wymaga jedynie znajomości dodawania. Jednak najciekawszym elementem szyfru jest tabela używana do szyfrowania.

Metoda szyfrowania

Tworzenie tabeli

Każda tabela szyfrowania składa się z 10 kolumn, których kolejne nagłówki to kolejne cyfry począwszy od 0. W pierwszym wierszu o pustym nagłówku wpisuje się maksymalnie z 10 znaków. Oznaczałoby to, że szyfr działałby tylko dla dziesięciu znaków.

Z tego powodu przyjmuje się, że niektóre pola mogą pozostać wolne. Wtedy dla każdej kolumny, która ma puste pole w pierszym wierszu tworzy się wiersz o nagłówku takim jak nagłówek odpowiadającej mu kolumny. Każdy wiersz prócz pierwszego musi być wykorzystany. Znaki wpisane do tabeli pozostają dowolne.

Przykład

Przyjmuje się, że dla alfabetu łacińskiego wystarczy w pierszym wierszu zostawić dla wolne pola, aby uzyskać tabelkę, którą można uzupełnić 28 znakami tj. 26 znaków alfabetu oraz dwa znaki dodatkowe. Zazwyczaj w pierwszym wierszu wpisuje się znaki najczęściej występujące w alfabecie.

Przykładowo tabelka wygląda następująco:

0123456789
AIE ONS RG
3MHYVDZKQ/T
7WCXFJBPL.U

Szyfrowanie

Na podstawie określonej tabelki można przejść do szyfrowania danych. W celu zaszyfrowania danych należy określić pewien klucz złożony z samych cyfr. Następnie należy zamienić każdą kolejną literę tekstu jawnego na odpowiadającą mu wartość. Wartość znaku określa się znajdując jego pozycję w tabeli, następnie zapisując koło siebie nagłówek wiersza i nagłówek kolumny koło siebie.

Przykładowo litera "A" ma wartość "0", a litera "B" ma odpowiednik "75".

Po zamianie tekstu jawnego na ciąg cyfr do każdej kolejnej litery dodaje się cyklicznie kolejną cyfrę z klucza. Jeśli wartość wynikowa nie jest cyfrą to należy zostawić jedność, a cyfrę dziesiątek odrzucić. Następnie na podstawie kolejnych cyfr należy odczytać znaki na podstawie tabeli. Należy pamiętać, że jeśli kolejna cyfra jest nagłówkiem, któregoś z wierszy to należy pobrać kolejną cyfrę i znaleźć pole, któremu odpowiada ta wartość.

Przykładowo w celu zamiany 312 na zaszyfrowany tekst to pierwsza cyfra 3 jest w tabelce nagłówkiem wiersza, więc szukamy pola, którego wartość to "31". W tym przypadku to pole z wartością "Y". Do zamiany pozostała wartość 2, więc jest ona wyszukiwana w pierwszym wierszu i jest to "E".

Przykład

Przypuśćmy, że szyfrowany jest tekst "TEKST" przy pomocy wcześniej utworzonej tabelki i klucza 121.

ZnakTEKST
Cyfry39236639
Klucz12112112
Suma41348741
WynikOIDRJI

Ostatecznie szyfrogramem jest "OIDRJI".

Implementacja

Klucz

Do wczytania klucza posłuży funkcja wczytajKlucz(), która wczyta dziesięc znaków i na tej podstawie określi wielkość tabeli docelowej. Za znak pustej tabeli przyjmuje się znak "#". Wszystkie dane zostaną zapisane do jednowymiarowej tabeli.

  1. char* wczytajTabele() {
  2.   char* data = new char[10];
  3.   int licznik = 0;
  4.   for (int i = 0; i < 10; i++) {
  5.     cin >> data[i];
  6.     if (data[i] == '#')
  7.       licznik++;
  8.   }

(2. - 8.) Wczytaj pierwszy wiersz tabeli i określ ile jest pustych pozycji. (9.) Na podstawie zebranych danych utwórz nową tabelę, aby można było pomieścić wszystkie dane i (11. - 12.) przepisz dotychczasowe dane, a następnie (13.) usuń niepotrzebną tabelę. (14. - 16.) Kolejny krok polega na wczytaniu pozostałych danych.

Szyfrowanie

Do zaszyfrowania danych należy podać trzy argumenty: tabela - tabelę używaną do szyfrowania, klucz - klucz użyty do szyfrowania oraz tekst - tekst jawny do zaszyfrowania.

  1. char* szyfruj(char* tabela, char* klucz, char* tekst) {
  2.   char* wposr = new char[strlen(tekst) * 2 + 1];
  3.   int indeks = 0;
  4.   for (int i = 0; tekst[i]; i++) {
  5.     int pozycja = npozycja(tabela, tekst[i], 0);
  6.     if (pozycja >= 10)
  7.       wposr[indeks++] = npozycja(tabela, '#', pozycja / 10);
  8.     wposr[indeks++] = pozycja % 10;
  9.   }
  10.   wposr[indeks] = 0;

Pierwsza część kodu polega na zamiania ciągu znaków na ciąg cyfr. Na początek (2.) deklarowna jest tablica do której zostaną zapisane cyfry. W tym przypadku może być ich maksymalnie po dwie do jednej litery. (3.) Zmienna indeks pozwoli śledzić ile cyfr zostało już zapisane w tabeli. (4.) Dla każdego znaku w tekście: (5.) znajdź jego pozycję w tablicy. (6.) Jeśli pozycja nie jest mniejsza od 10 to znaczy, że wiersz ma nagłówek. (7.) Można go znaleźć określając numer wiersza i szukając tego wystąpienia # w tablicy. (8.) Za każdym razem natomiast należy dopisać numer kolumny w której znajduje się sprawdzany znak. Na koniec pierwszej części (10.) dopisywane jest dodatkowe 0.

  1.   for (int i = 0; i < indeks; i++)
  2.     wposr[i] = (wposr[i] + klucz[i % strlen(klucz)] - '0') % 10;
  3.   char* wynik = new char[indeks * 2 + 1];
  4.   int j = 0;
  5.   for (int i = 0; i < indeks; i++) {
  6.     if (tabela[wposr[i]] != '#') {
  7.       wynik[j++] = tabela[wposr[i]];
  8.     } else {
  9.       int wiersz = ileoddo(tabela, '#', 0, wposr[i]);
  10.       int kolumna = wposr[i + 1];
  11.       wynik[j++] = tabela[wiersz * 10 + kolumna];
  12.       i++;
  13.     }
  14.   }
  15.   wynik[j++] = '\0';
  16.   return wynik;
  17. }

Kolejny etap polega na (11. - 12.) na przesunięciu każdej wartości o odpowiednią wartość z klucza. Potem na podstawie zebranych danych można (13.) zadeklarować tablice do której zapisany zostanie szyfrogram. (15. - 24.) Dla każdej kolejnej cyfry: (16.) określ czy ma być pobrana z pierwszego wiersza.

Wyszukiwanie n-tego wystąpienia znaku

W celu znalezienia indeksu pod którym wartosc znajduje się w tablicy data po raz ktory można skorzystać z funkcji npozycja(), której kod został przedstawiony poniżej.

  1. int npozycja(char* data, int wartosc, int ktory) {
  2.   for (int i = 0; data[i]; i++) {
  3.     if (data[i] == wartosc) {
  4.       ktory--;
  5.       if (ktory <= 0)
  6.         return i;
  7.     }
  8.   }
  9.   return -1;
  10. }

Ile wystąpień w zakresie

Poniższa funkcja ileoddo() sprawdza ile w tablicy data jest wystąpień wartosc w zakresie [zakres_od, zakres_do].

  1. int ileoddo(char* data, int wartosc, int zakres_od, int zakres_do) {
  2.   int licznik = 0;
  3.   for (; zakres_od <= zakres_do; zakres_od++)
  4.     if (data[zakres_od] == wartosc)
  5.       licznik++;
  6.   return licznik;
  7. }

Testowanie funkcji

Przykładowo w celu przetestowania działania napisanych funkcji można skorzystać z poniższej funkcji main():

  1. int main() {
  2.   cout << "Podaj tabele: " << endl;
  3.   char* tabela = wczytajTabele();
  4.   cin.ignore();
  5.   cout << "Podaj klucz: " << endl;
  6.   char* klucz = new char[32];
  7.   cin.getline(klucz, 32);
  8.   cout << "Podaj tekst: " << endl;
  9.   char* tekst = new char[512];
  10.   cin.getline(tekst, 512);
  11.   char* tekstZ = szyfruj(tabela, klucz, tekst);
  12.   cout << "Szyfrogram to: " << tekstZ << endl;
  13.   delete tabela, klucz, tekstZ;
  14.   system("pause");
  15.   return 0;
  16. }

W celu wywołania przykładu opisanego na początku artykułu należy wpisać kolejno:

  1. AIE#ONS#RGMHYVDZKQ/TWCXFJBPL.U
  2. 121
  3. TEKST