Реализация на php алгоритма шифра ГОСТ 28147—89. С работой скрипта можно ознакомиться здесь.
Исходный код:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Криптография - шифр ГОСТ</title>
</head>
<body>
<form method="post">
Синхропосылка: <input name="s" type="text" value=""/><br />
Исходный текст:<br />
<textarea rows="10" cols="40" name="text">гидродинамический удар по данным астрономических наблюдений растягивает плазменный луч генерируя периодические импульсы синхротронного излучения при погружении в жидкий кислород расслоение стабилизирует субсветовой погранслой тем самым открывая возможность цепочки квантовых превращений тело несмотря на некоторую вероятность коллапса мономолекулярно усиливает фронт при этом дефект массы не образуется</textarea><br />
<input type="submit" name="go" value="Шифровать!" />
</form>
<?php
global $Sblock, $K32;
//таблица замен - просто константные значения
$Sblock = array (
array (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3),
array (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9),
array (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11),
array (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3),
array (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2),
array (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14),
array (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12),
array (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12)
);
//Ключ - 256 бит - просто константные значения
$K32 =
array (
0x10CBC8CD,
0x2FC0CFC5,
0x34C0CCC1,
0x92CEC2C8,
0x81CCD8C3,
0x705DCDC8,
0x64C8D8C2,
0x5ACECBDF
);
// базовые циклы
/* В их обозначении число (32 или 16) указывает на число
повторений основного шага, буква (З или Р) указывает на
разновидность порядка использования элементов ключа.*/
//цикл зашифрования
function base_32z($N){
global $K32;
for($k=1;$k<=3;$k++)
for($i=0;$i<=7;$i++){
$N = base($N,$K32[$i]);
}
for($i=7;$i>=0;$i--){$N = base($N,$K32[$i]);}
$N = substr($N,32).substr($N,0,32);
return $N;
}
//цикл расшифрования
function base_32r($N){
global $K32;
for($i=0;$i<=7;$i++){$N = base($N,$K32[$i]);}
for($k=1;$k<=3;$k++)
for($i=7;$i>=0;$i--){
$N = base($N,$K32[$i]);
}
$N = substr($N,32).substr($N,0,32);
return $N;
}
//цикл выработки имитовставки
function base_16z($N){
for($k=1;$k<=2;$k++)
for($i=0;$i<=7;$i++){
$N = base($N,$K32[$i]);
}
return $N;
}
// моя функция. для больших чисел
function mod($a,$b=4294967296){ // 4294967296 == 2^32
while($c<$a){
$c_old=$c;
$c+=$b;
}
return $a-$c_old;
}
// дополняем нулями
function addzero($a,$b=4,$c=0){ // а - число которое дополняем, b - до скольки, с - слева=0/справа=1
while(strlen($a)<$b){
if(!$c){$a='0'.$a;}else{$a.='0';}
}
return $a;
}
//основной шаг криптопреобразования
function base($N,$X){
/*N – преобразуемый 64-битовый блок данных, в ходе выполнения шага его младшая (N 1) и старшая (N 2) части обрабатываются как отдельные 32-битовые целые числа без знака. Таким образом, можно записать N=(N 1,N 2).
X – 32-битовый элемент ключа;*/
$N = array(substr($N,0,32), substr($N,32));
$N[0] = base_convert($N[0],2,10);
$X = base_convert($X,16,10);
/* Шаг 1
Сложение с ключом. Младшая половина преобразуемого блока складывается по модулю 232 с используемым на шаге элементом ключа, результат передается на следующий шаг;*/
$tmp = mod(($N[0] + $X)); // , 4294967296
/* Шаг 2
Поблочная замена. 32-битовое значение, полученное на предыдущем шаге, интерпретируется как массив из восьми 4-битовых блоков кода: S=(S 0, S 1, S 2, S 3, S 4, S 5, S 6, S 7), причем S 0 содержит 4 самых младших, а S 7 – 4 самых старших бита S.
Далее значение каждого из восьми блоков заменяется новым, которое выбирается по таблице замен следующим образом: значение блока Si меняется на Si -тый по порядку элемент (нумерация с нуля) i-того узла замены (т.е. i-той строки таблицы замен, нумерация также с нуля). Другими словами, в качестве замены для значения блока выбирается элемент из таблицы замен с номером строки, равным номеру заменяемого блока, и номером столбца, равным значению заменяемого блока как 4-битового целого неотрицательного числа. Отсюда становится понятным размер таблицы замен: число строк в ней равно числу 4-битовых элементов в 32-битовом блоке данных, то есть восьми, а число столбцов равно числу различных значений 4-битового блока данных, равному как известно 24, шестнадцати.*/
$tmp = base_convert($tmp,10,2);
// дополняем нулями
$tmp = addzero($tmp,32);
$tmp = strrev($tmp); // чтобы получить обратный порядок в массиве
$S = array();
for($i=0;$i<=7;$i++) $S[$i] = substr($tmp,4*$i,4);
global $Sblock;
// заменяем
for($i=0;$i<=7;$i++){
$S[$i] = addzero( base_convert( $Sblock[$i][base_convert($S[$i],2,10)], 10, 2) );
}
/* Шаг 3
Циклический сдвиг на 11 бит влево. Результат предыдущего шага сдвигается циклически на 11 бит в сторону старших разрядов и передается на следующий шаг. На схеме алгоритма символом обозначена функция циклического сдвига своего аргумента на 11 бит влево, т.е. в сторону старших разрядов.*/
$tmp = implode('', $S);
// "сдвигаем"
$S = substr($tmp,11).substr($tmp,0,11);
/* Шаг 4
Побитовое сложение: значение, полученное на шаге 3, побитно складывается по модулю 2 со старшей половиной преобразуемого блока.*/
$tmp = '';
for($i=0;$i<32;$i++){
$tmp .= ($S[$i] + $N[1][$i]) % 2;
}
/* Шаг 5
Сдвиг по цепочке: младшая часть преобразуемого блока сдвигается на место старшей, а на ее место помещается результат выполнения предыдущего шага. */
$N = $tmp.addzero(base_convert($N[0],10,2), 32);
/* Шаг 6
Полученное значение преобразуемого блока возвращается как результат выполнения алгоритма основного шага криптопреобразования.*/
return $N;
}
if(isset($_POST['go'])){
$text = $_POST['text'];
echo "Длина исходного текста: ".strlen($text)."<br />";
// разбаваем текст на блоки по 8 байт
$tmp_text = array();
while($p<strlen($text)){
$tmp_text[] = substr($text, $p, 8);
$p+=8;
}
// Шифруем
function text2bin($text){
$tmp_text = '';
for($i=0;$i<strlen($text);$i++){
$tmp_text .= addzero( base_convert( ord($text[$i]),10,2 ), 8 );
}
return $tmp_text;
}
for($i=0;$i<count($tmp_text);$i++){
$summ = '';
$a = 0;
$summ = text2bin($tmp_text[$i]);
$cryptogram .= base_32z($summ);
}
echo "<br />Простая замена:<br />";
$text = '';
$a = 0;
while($a<strlen($cryptogram)){
base_convert( substr($cryptogram,$a,8), 2, 10)."<br />";
$text .= chr( base_convert( substr($cryptogram,$a,8), 2, 10) );
$a+=8;
}
echo $text;
echo "<br />(длина: ".strlen($text).")";
echo "<br />";
// Расшифровываем!
$text='';
$a = 0;
while($a<strlen($cryptogram)){
$text .= base_32r(substr($cryptogram,$a,64));
$a+=64;
}
$cryptogram = $text;
echo "<br /><br />Простая замена (расшифрованный):<br />";
$text='';
$a = 0;
while($a<strlen($cryptogram)){
base_convert( substr($cryptogram,$a,8), 2, 10)."<br />";
$text .= chr( base_convert( substr($cryptogram,$a,8), 2, 10) );
$a+=8;
}
echo $text;
echo "<br />(длина: ".strlen($text).")";
# Гамирование
function gammirovanie($T,$S=false){
/* Шаг 0
Определяет исходные данные для основного шага криптопреобразования:
T о(ш) – массив открытых (зашифрованных) данных произвольного размера, подвергаемый процедуре зашифрования (расшифрования), по ходу процедуры массив подвергается преобразованию порциями по 64 бита;
S – синхропосылка , 64-битовый элемент данных, необходимый для инициализации генератора гаммы;*/
// разбаваем текст на блоки по 8 байт
$tmp_text = array();
$p = 0;
while($p<strlen($T)){
$tmp_text[] = text2bin( substr($T, $p,
);
$p+=8;
}
$T = $tmp_text;
// генерим синхропосылку
global $SS;
if($S===false){
$SS = $S = substr(md5(rand(0, 666)), 0, 8);
}
echo "(Синхропосылка: $S):";
echo "<br />";
/* Шаг 1
Начальное преобразование синхропосылки, выполняемое для ее «рандомизации», то есть для устранения статистических закономерностей, присутствующих в ней, результат используется как начальное заполнение РГПЧ;*/
// преобразуем в двоичное
$tmp_text = '';
for($i=0;$i<strlen($S);$i++){
$tmp_text .= text2bin($S[$i]);
}
$S = base_32z($tmp_text);
/* Шаг 2
Один шаг работы РГПЧ, реализующий его рекуррентный алгоритм. В ходе данного шага старшая (S 1) и младшая (S 0) части последовательности данных вырабатываются независимо друг от друга;*/
$C = array();
$C[0] = 0x1010101;
$C[1] = 0x1010104;
$C[0] = base_convert($C[0], 16, 10);
$C[1] = base_convert($C[1], 16, 10);
$S = array( base_convert(substr($S,0,32), 2, 10), base_convert(substr($S,32), 2, 10) );
for($i=0;$i<count($T);$i++){
$S[0] = mod($S[0] + $C[0]);
$S[1] = mod( ($S[1] + $C[1] - 1), 4294967296-1) + 1;
/* Шаг 3
Гаммирование. Очередной 64-битовый элемент, выработанный РГПЧ, подвергается процедуре зашифрования по циклу 32–З, результат используется как элемент гаммы для зашифрования (расшифрования) очередного блока открытых (зашифрованных) данных того же размера.*/
$tmp = '';
$S_tmp = addzero( base_convert($S[0],10,2), 32).addzero( base_convert($S[1],10,2), 32);
$S_tmp = base_32z($S_tmp);
for($a=0;$a<strlen($T[$i]);$a++){
$tmp .= ( $T[$i][$a] + $S_tmp[$a] ) % 2;
}
$T[$i] = $tmp;
}
/* Шаг 4
Результат работы алгоритма – зашифрованный (расшифрованный) массив данных.*/
return $T = implode("", $T);
}
if($_POST['s']!='') $SS=$_POST['s']; else $SS=false;
echo "<br /><br /><br />Гаммирование, зашифрованный текст ";
$T = gammirovanie($_POST['text'],$SS);
echo "<br />";
// Перегоняем снова в текст!
$text='';
$a = 0;
while($a<strlen($T)){
$text .= chr( base_convert( substr($T,$a,8), 2, 10) );
$a+=8;
}
echo $text;
echo "<br />";
echo " длина: ".strlen($text);
echo "<br />";
echo "<br />";
$T = $text;
echo "<br />Гаммирование, расшифрованный текст ";
$T = gammirovanie($T, $SS);
echo "<br />";
// Перегоняем снова в текст!
$text='';
$a = 0;
while($a<strlen($T)){
$text .= chr( base_convert( substr($T,$a,8), 2, 10) );
$a+=8;
}
echo $text;
echo "<br />";
echo " длина: ".strlen($text);
echo "<br />";
echo "<br />";
}
?>
</body>
</html>
UPD 23/03/2012
Сегодня заметил много переходов с контакта людей учащихся на АВТФ. Ребятки, мой вам совет, не пытайтесь подсунуть найденное здесь Минину
Он вас сразу раскусит и лучше от этого точно не будет. Используйте как возможность понять принцип работы алгоритма. Также почитайте комменты. Моя реализация не совсем корректно работает. В комментариях есть ссылки на другие версии.
И это, посоветуйте мой сайт http://ekl.mn своим друзьям и знакомым
Вам он после универа тоже пригодиться!

А есть реализация этого алгоритма на С++?
Конечно есть. Скачать можно отсюда http://www.ssl.stu.neva.ru/psw/crypto/appl_rus/appl_cryp.htm Прямая ссылка: http://www.ssl.stu.neva.ru/psw/crypto/appl_rus/ac_source.pdf
Благодарю!
Cпаааааааааасиииииибоооооо!!!!)))))
а какое гаммирование?с обратной связью?
Да
Добрый день, Дмитрий! Надеюсь на Вашу помощь. Мне нужно реализовать этот алгоритм (режима простой замены хватит), но чтобы можно было указать ключ и можно было отдельно шифровать/расшифровать. В php не шарю вообще.
Вы можете мне как-нибудь помочь?
Здравствуйте, Игорь. Вам на PHP нужно реализовать? Если так, то вам просто нужно оставить код до 221 строки и после 325.
После 8 строки добавить:
Это будет поле для ввода ключа, в него нужно будет ввести подобные значения через запятую: 0x10CBC8CD, 0x2FC0CFC5, 0x34C0CCC1, 0x92CEC2C8, 0x81CCD8C3, 0x705DCDC8, 0x64C8D8C2, 0x5ACECBDF
После 11 строки добавить:
Строки с 30 по 40 заменить кодом:
$K32 = explode(‘,’,$_POST['key']);
foreach($K32 as $k=>$v) $K32[$k] = (float)$v;
Перед строкой:
// Расшифровываем!
Вставить:
} if(isset($_POST['go2'])){
Строку номер 202 заменить на:
$text = $_POST['text'];
Вроде все.
Чтобы не запутаться в строках изменения рекомендую вносить снизу вверх. Честно код не проверял, но по идее должно работать.
234 $tmp_text[] = text2bin( substr($T, $p, );
что значит эта строка? картинка к чему?
279 $tmp .= ( $T[$i][$a] + $S_tmp[$a] ) % 2;
выскакивает notice: unitialized string offset: (от 65 до бесконечности)
234 строка должна выглядеть следующим образом:
$tmp_text[] = substr($text, $p, 8);
Просто wp посчитал нужным заменить «8)» на смайлик.
про ошибку на 279 строке сказать ничего не могу, но она по идее должна пропасть после внесения исправлений на 234 строке.
Дмитрий, а Вы пробовали шифровать текст своей реализацией на PHP, а расшифровывать программой на C++?
Я пробовал — не получилось ни одной из найденных реализаций.
Честно — не пробовал. Я так понимаю ключ и таблица замен использовались одинаковые? Есть уверенность в том что текст из браузера скопировался и вставился в другие проги нормально без потери символов? Просто для чистоты эксперимента по идее нужно бы кодировать полученный шифр в обычный текст, а к других прогах раскодировать обратно. Для других реализаций получается проделывать подобный эксперимент не совсем понял?
Этот код в его исходном состоянии является нерабочим из-за мелких багов. К примеру к $c дописывается значение (с помощью .=), хотя само $c не проинициализировалось.
И ещё. Почему длина исходного текста больше, чем длина зашифрованного и расшифрованного текста? При чём как раз ровно на столько сколько пробелов в тексте. Опять же в алгоритме баг. Или я чего-то не досмотрел?
А вообще за алгоритм большое спасибо. Он мне очень помог. Автору респект.
Ах да, вот сайт, где на пальцах показано как шифрует этот алгоритм. http://wasm.ru/article.php?article=gost29147-89 Может кому-то понадобится.
Спасибо большое за комментарий.
По первому пункту — это просто зависит от уровня вывода ошибок в PHP. У меня просто стоит обычно значение не выводить Notice.
А по второму пункту, честно, уже не помню точностей работы алгоритма, а времени снова вникать пока нету. Замечание интересное и хорошее. В любом случае оно пригодиться другим кто захочет разобраться конкретно в этой реализации.
В своем блоге я описывал подобную реализацию. Там же в комментариях, с человеком разбирали ошибки в Вашем скрипте. Я думаю вам будет полезно почитать:
Реализация шифра ГОСТ 28147-89 на PHP
Спасибо за комментарий и за найденные ошибки. Ознакомился. Единственное, хочу заметить, что то, что у вас было задано как курсовик, у нас было одной из 4х лаб
Ну тут уж кто на кого учится)