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

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



Шифр Виженера является полиалфавитным и представляет собой последовательность шифров Цезаря с различным значением сдвига. Например, первый символ кодируем со сдвигом на 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. Да, код не оптимален, но, имхо, в таком виде будет проще со стороны разобраться в нем и модифицировать под свои нужды.
+4
13 декабря 2010, 11:12
2
GomelHawk 6,0

комментарии (21)

0
evilbloodydemon #
криптография — такое дело, где не надо ничего выдумывать, если ты, конечно, не криптограф. стойкость систем, основанных на замене, равна нулю. любых самодельных — еще меньше =)
0
GomelHawk #
Это понятно, что все можно в конце концов взломать — вопрос средств и времени.
Просто понадобилось нечто среднее между совсем просто и совсем сложно.
+1
vk2 #
–1
GomelHawk #
Во-первых хотелось чего-нибудь своего, ну а во-вторых чтобы была возможность грубо говоря «на коленке» провести шифрование/дешифрацию подручными средствами. В моем случае достаточно иметь распечатку аналога приведенного в статье изображения (применительно к используемому алфавиту).
+1
vk2 #
ну тогда да.

в конце концов, мы же не знаем, зачем Вам это нужно — может, Вы онлайн-игру в пиратов для детей делаете, где оффлайн-расшифровка просто подразумевается.
0
evilbloodydemon #
ну так и беда-то в том, что вы думаете, что у вас есть хоть какая-то защита, а её нет вовсе
0
GomelHawk #
У Вас имеется некий алгоритм легкого взлома? С удовольствием ознакомлюсь.
0
evilbloodydemon #
0
GomelHawk #
Метод Касиски применим в случае наличия в кодированном тексте повторяющихся последовательностей для последующего определения длины ключа. То бишь нужен достаточно длиный текст, причем желательно художественный, чтобы использовать частотное распределение. В случае достаточно коротких текстов и длинных контрольных слов метод практически неприменим.
В применительности к моему варианту — для усложнения подбора указанным методом достаточно использовать длинный ключ. Как дополнительные меры противодействия можно увеличить длину случайных символов. Да и собственно из исходного набора буквоцифр достаточно изъять некоторые (они будут вставлены в результат без конвертации). после этого метод Касиски практически не страшен.
0
evilbloodydemon #
опасно недооценивать криптоанализ. полиалфавитные шифры уязвимы, нужно использовать стойкие решения, остальное — трата времени. nuff said.
0
GomelHawk #
Nuff said.)
0
Midas #
Метод Касиски страшен.

Если вы не будете менять ключ. Нагенерите много коротких шифрограмм.
Их можно будет подвергнуть частотному анализу.
+1
Sannis #
Простите, но у вас ужасное форматирование кода.
–2
GomelHawk #
Что есть то есть. Дело привычки.
+1
ThePretender #
А зачем вы добавляете префикс “vzh” перед именем каждой переменной? Код абсолютно нечитаемый ИМХО.
+1
GomelHawk #
Виноват, каюсь. Уже исправил.
0
SkazochNik #
А правда у разведчиков есть корочка? У вас есть?
0
GomelHawk #
Нет и совершенно не жалею об этом :)
0
lolopolosko #
Цитирую:
«результат всегда будет выглядеть одинаково при кодировании («ИВАНОВ» всегда будет «ИДЕЮАВ»)»

Вопрос почему? Ведь если вы зададите другой ключ тогда результат будет другой… или я не прав?
И к тому же если «Иванов» будет смещен на несколько символов (пробелов), результат будет другим!
0
GomelHawk #
Цитирую:

Особенно критична указанная особенность для малых сообщений и в случаях, когда длина кодового слова равна длине часто повторяющихся моментов в тексте.

0
avn #
Вообще говоря, ключ Вижинера является самым устойчивым к взлому при выполнении следующих условий: 1) ключ не короче сообщения, 2) ключ случаен, 3) ключ меняется от сообщения к сообщению. Устойчивость здесь определяется случайностью ключа: АБСОЛЮТНО случайный ключ (при выполнении прочих условий) делает текст НЕВЗЛАМЫВАЕМЫМ (цена взлома — создание сообщения с нуля).
Реализация (для цифровой техники — в частности, компьютера, контроллера) элементарная: шифрованный_текст = исходный_текст XOR ключ. Расшифровка та же самая: исходный_текст = шифрованный_текст XOR ключ.
В реальности он и используется в самых ответственных случаях.
Вся проблема тут для криптографии в АБСОЛЮТНОЙ случайности, которая, увы, не существует…

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.