Яндекс.Метрика

    Поумничаем

    Модификация шифра Виженера

    Недавно в личных целях понадобилось использовать некий простой шифр для сокрытия информации. Изначально хотел использовать старый добрый шифр Цезаря, где каждый символ заменяется другим, сдвинутым на фиксированное число позиций (например, при сдвиге на одну позицию получаем АРКА -> БСЛБ). Однако, в случае шифрования небольших объемов информации и в случаях наличия в исходном тексте повторяющихся букв шифр взламывается за считанные секунды (например, в нашем случае сдвига на единицу «ООО БАРК» -> «ППП ВБСЛ»). В результате выбор пал на шифр Виженера, достаточно простой в реализации и более стойкий ко взлому…


    Шифр Виженера является полиалфавитным и представляет собой последовательность шифров Цезаря с различным значением сдвига. Например, первый символ кодируем со сдвигом на 3, второй — на 5, третий — на 8 и т.д. Числовую последовательность значений сдвига запоминаем с помощью кодового слова, где позиция соответствующей буквы в исходном алфавите будет означать искомое значение сдвига. Так, для кодового слова «АВЕРС» первый символ нашего текста будем шифровать без сдвига («А»-0), второй — со сдвигом на 2 («В»-2)… пятый — со сдвигом на 18 («С»-18), шестой — снова без сдвига («А») и т.д. В результате для кодового слова «АВЕРС» получаем «ПАРУС» -> «ПВХДГ».

    Все вроде бы хорошо, криптостойкость повысилась, повторяющиеся буквы уже не представляют опасность («ООО БАРК» -> «ОРУ ССРМ»), однако появляется другой момент, связанный с особенностями шифра: результат всегда будет выглядеть одинаково при кодировании («ИВАНОВ» всегда будет «ИДЕЮАВ»). Особенно критична указанная особенность для малых сообщений и в случаях, когда длина кодового слова равна длине часто повторяющихся моментов в тексте. Если с последним можно успешно бороться увеличивая длину кодового слова, то с повторяемостью результата при кодировании одинакового текста здесь уже ничего не сделать.

    Выход для себя я нашел в добавочном случайном первом символе, точнее цифре, выполняющей двойственную функцию. Во-первых обозначает количество случайных добавочных символов к исходному тексту, а во-вторых — это величина сдвига кодового слова для кодирования остального текста («0» — «АВЕРС», «1» — «ВЕРСА», «2» — «ЕРСАВ» и т.д.). В результате получаем плавающую длину результата (за счет добавления случайных символов) и отличающиеся последовательности при шифровании идентичных текстов.

    Для интересующихся привожу код на PHP:

    
    function vizhener_encode($text,$kod) // кодирование, простой метод
     { $kod=strtoupper($kod); $string=strtoupper($text); $enc = array(); $dec = array(); 
       $str="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
       for($i=0;$i<strlen($str);$i++)
        { for($j=0;$j<strlen($str);$j++)
           { $ij=$i+$j; if($ij>=strlen($str)) { $ij=$ij-strlen($str); }
    	 $enc[$str{$i}][$str{$j}]=$str{$ij};
    	 $dec[$str{$i}][$str{$ij}]=$str{$j};
           }
        }
       $pos=0; $result=""; $string=eregi_replace(" ","_",$string);
       for($i=0;$i<strlen($string);$i++)
        { if(!eregi($string{$i},$str)) { $result=$result.$string{$i}; }
          else
           { $result=$result.$enc[$kod{$pos}][$string{$i}]; $pos=$pos+1;
    	 if($pos>=strlen($kod)) { $pos=$pos-strlen($kod); } 
           }
        }   
       return $result;
     }
    
    function vizhener_encode_mod($text,$kod) // кодирование, модифицированный метод
     { $kod=strtoupper($kod); $string=strtoupper($text); $enc = array(); $dec = array();  
       $str="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
       $add=mt_rand(1,9); $string=$string;
       for($i=1;$i<=$add;$i++) { $string=$string.$str{mt_rand(0,strlen($str))}; }
       for($i=0;$i<strlen($str);$i++)
        { for($j=0;$j<strlen($str);$j++)
           { $ij=$i+$j; if($ij>=strlen($str)) { $ij=$ij-strlen($str); }
    	 $enc[$str{$i}][$str{$j}]=$str{$ij};
             $dec[$str{$i}][$str{$ij}]=$str{$j};
           }
        }
       $pos=0; $result=""; $string=eregi_replace(" ","_",$string);
       $pos=$pos+$add; while($pos>=strlen($kod)) { $pos=$pos-strlen($kod); }
       if($pos<0) { $pos=$pos+strlen($kod); }
       for($i=0;$i<strlen($string);$i++)
        { if(!eregi($string{$i},$str)) { $result=$result.$string{$i}; }
          else
           { $result=$result.$enc[$kod{$pos}][$string{$i}]; $pos=$pos+1;
    	 if($pos>=strlen($kod)) { $pos=$pos-strlen($kod); }
           }
        }
       $result=$enc[$kod{0}][$add].$result;
       return $result;
     }
    
    function vizhener_decode($text,$kod) // декодирование, простой метод
     { $kod=strtoupper($kod); $string=strtoupper($text); $enc = array(); $dec = array(); 
       $str="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
       for($i=0;$i<strlen($str);$i++)
        { for($j=0;$j<strlen($str);$j++)
           { $ij=$i+$j; if($ij>=strlen($str)) { $ij=$ij-strlen($str); }
    	 $enc[$str{$i}][$str{$j}]=$str{$ij};
    	 $dec[$str{$i}][$str{$ij}]=$str{$j};
           }
        }
       $pos=0; $result=""; $string=eregi_replace(" ","_",$string);
       for($i=0;$i<strlen($string);$i++)
        { if(!eregi($string{$i},$str)) { $result=$result.$string{$i}; }
          else
           { $result=$result.$dec[$kod{$pos}][$string{$i}]; $pos=$pos+1;
    	 if($pos>=strlen($kod)) { $pos=$pos-strlen($kod); }
           }
        }
       return $result;
     }
    
    function vizhener_decode_mod($text,$kod) // декодирование, модифицированный метод
     { $kod=strtoupper($kod); $string=strtoupper($text); $enc = array(); $dec = array(); 
       $str="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
       for($i=0;$i<strlen($str);$i++)
        { for($j=0;$j<strlen($str);$j++)
           { $ij=$i+$j; if($ij>=strlen($str)) { $ij=$ij-strlen($str); }
    	 $enc[$str{$i}][$str{$j}]=$str{$ij};
    	 $dec[$str{$i}][$str{$ij}]=$str{$j};
           }
        }
       $pos=0; $result=""; $string=eregi_replace(" ","_",$string);
       $add=$dec[$kod{0}][$string{0}];
       $pos=$pos+$add; while($pos>=strlen($kod)) { $pos=$pos-strlen($kod); }
       if($pos<0) { $pos=$pos+strlen($kod); }
       for($i=1;$i<(strlen($string)-$add);$i++)
        { if(!eregi($string{$i},$str)) { $result=$result.$string{$i}; }
          else
           { $result=$result.$dec[$kod{$pos}][$string{$i}]; $pos=$pos+1;
    	 if($pos>=strlen($kod)) { $pos=$pos-strlen($kod); }
           }
        }
       return $result;
     }
    



    P.S. Да, код не оптимален, но, имхо, в таком виде будет проще со стороны разобраться в нем и модифицировать под свои нужды.