Пользователь
0,0
рейтинг
17 января 2010 в 18:54

Разработка → ГОСТ 28147-89 (Часть 2. Режим простой замены)

Эта статья является продолжением статьи про ГОСТ 28147-89. Как уже говорилось ранее, ГОСТ 28147-89 поддерживает четыре режима работы, но, пожалуй, главным из них является режим простой замены, который используется как самостоятельно, так и как составная часть других режимов.
В статье приведен код на c++, реализующий данный режим.
Чтобы понять, как работает ГОСТ 28147-89 в данном режиме, необходимо рассмотреть следующую схему:

Собственно, суть

Зашифрование

  1. Открытые данные разбиваются на блоки по 64 бита.
  2. Далее производится ввод первого блока в накопители N1 и N2. При этом биты открытой информации вводятся следующим образом: 1-й бит открытой информации — в 1-й разряд накопителя N1, ..., 32-й — в 32-й разряд накопителя N1, 33-й — в 1-й разряд накопителя N2 и так далее, пока 64-й бит открытой информации не будет введен в 32-й разряд накопителя N2.
  3. В КЗУ вводится ключ длиной 256 бит способом, рассмотренным в статье ГОСТ 28147-89 (Часть 1. Введение и общие принципы).
  4. Производится зашифрование открытых данных в режиме простой замены (в 32 цикла):

    1. В первом цикле содержимое регистра N1 суммируется с заполнением X0 из КЗУ по модулю 232 в сумматоре СМ1.
    2. В блоке подстановки K производится замена 32 бит информации, поступившей из сумматоре СМ1 по правилам, рассмотренным в первой статье.
    3. В регистре сдвига R осуществляется циклический сдвиг на 11 в сторону старшего разряда.
    4. Информация с регистра сдвига R и накопителя N2 суммируется по модулю 2 в сумматоре СМ2.
    5. Старое заполнение накопителя N1 переписывается в накопитель N2.
    6. Результат с выхода сумматора СМ2 переписывается в накопитель N1.
    7. Первый цикл заканчивается.
  5. Последующие циклы аналогичны первому, с тем лишь отличием, что во 2-м цикле вводится ключ X1, в 8-м — X7, в 9-м — X0 и так далее в том же порядке до 24 цикла. С 25 по 32 цикл ключ вводится в обратном порядке: X7 — в 25-м, X0 — в 32-м.
  6. После 32-го цикла в N1 информация сохраняется, а вот результат с выхода сумматора СМ2 переписывается в N2.
  7. Заполнение N1 и N2 и есть первый блок зашифрованных данных.
  8. Следующие блоки зашифровываются аналогично.

Расшифрование

Расшифрование осуществляется по тому же алгоритму, что и зашифрование, только на вход накопителей N1 и N2 поступают разбитые на блоки по 64 бита зашифрованные данные.Важным отличием является еще и то, что в прямом порядке (с X0 по X7) ключ вводится только в первых 8 циклах РПЗ, в остальных — в обратном (с X7 по X0).А так, после прохождения 32 циклов в накопителях N1 и N2 содержатся блоки открытых данных.

Поясним все вышесказанное кодом на C++

Данный код писался в консольном режиме в C++ Builder 6 достаточно давно и не претендует на звание самого лучшего, так что не бейте сильно, оптимизировать нет времени. Просто, он работает, причем, довольно быстро.
gost28147.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#include <stdio.h>
#include <conio.h>

//---------------------------------------------------------------------------

// взято из хелпа, определяем размер файла
long filesize(FILE *stream)
{
 long curpos, length;
 curpos = ftell(stream);
 fseek(stream, 0L, SEEK_END);
 length = ftell(stream);
 fseek(stream, curpos, SEEK_SET);
 return length;
}

// функция, реализующая работу ГОСТ 28147-89 в режиме простой замены
void rpz(int rezh, char* opener, char* saver)
{
 FILE *f_begin, *f_end; // потоки для исходного и конечного файлов

 // таблица замен
 byte Tab_Z[8][16] =
 {
  0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
  0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
  0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
  0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
  0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
  0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
  0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
  0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF
 };

// ключик
 unsigned long key[8] =
 {
  0x0123,
  0x4567,
  0x89AB,
  0xCDEF,
  0x0123,
  0x4567,
  0x89AB,
  0xCDEF
 };

 char N[4]; // 32-разрядный накопитель,

 unsigned long n1=0, n2=0, SUM232=0; // накопители N1, N2, и сумматор

 // открываем файлы
 f_begin = fopen (opener,"rb");
 f_end = fopen (saver,"wb");

 // определим количество блоков
 float blokoff;
 blokoff = 8*filesize(f_begin);
 blokoff = blokoff/64;
 int block = blokoff;
 if (blokoff-block>0) block++;

 int sh;
 if (filesize(f_begin)>=4) sh = 4; else sh = filesize(f_begin);
 int sh1 = 0;
 int flag=0;

 // начнем считывание и преобразование блоков
 // присутствуют проверки на полноту блоков, чтобы считать только нужное количество бит
 for (int i=0; i<block; i++)
 {
  // записываем в накопитель N1
  for (int q=0; q<4; q++) *((byte *)&N+q) = 0x00;
  if ((sh1+sh)<filesize(f_begin))
  {
   fread (N,sh,1,f_begin);
   sh1+=sh;
  }
  else
  {
   sh=filesize(f_begin)-sh1;
   fread (N,sh,1,f_begin);
   flag=1;
  }
  n1 = *((unsigned long *)&N);

  // записываем в накопитель N2
  for (int q=0; q<4; q++) *((byte *)&N+q) = 0x00;
  if ((sh1+sh)<filesize(f_begin))
  {
   fread (N,sh,1,f_begin);
   sh1+=sh;
  }
  else
  {
   if (flag==0)
   {
    sh=filesize(f_begin)-sh1;
    fread (N,sh,1,f_begin);
   } 
  }
  n2 = *((unsigned long *)&N);

  // 32 цикла простой замены
  // ключ считываем в требуемом ГОСТом порядке
  int c = 0;
  for (int k=0; k<32; k++)
  {
   if (rezh==1) { if (k==24) c = 7; }
    else { if (k==8) c = 7; }

   // суммируем в сумматоре СМ1
   SUM232 = key[c] + n1;

   // заменяем по таблице замен
   byte first_byte=0,second_byte=0,zam_symbol=0;
   int n = 7;
   for (int q=3; q>=0; q--)
   {
    zam_symbol = *((byte *)&SUM232+q);
    first_byte = (zam_symbol & 0xF0) >> 4;
    second_byte = (zam_symbol & 0x0F);
    first_byte = Tab_Z[n][first_byte];
    n--;
    second_byte = Tab_Z[n][second_byte];
    n--;
    zam_symbol = (first_byte << 4) | second_byte;
    *((byte *)&SUM232+q) = zam_symbol;
   } 
   
   SUM232 = (SUM232<<11)|(SUM232>>21); // циклический сдвиг на 11
   SUM232 = n2^SUM232; // складываем в сумматоре СМ2

   if (k<31)
   {
    n2 = n1;
    n1 = SUM232;
   }
   if (rezh==1)
   {
    if (k<24)
    {
     c++;
     if (c>7) c = 0;
    }
    else
    {
     c--;
     if (c<0) c = 7;
    }
   }
   else
   {
    if (k<8)
    {
     c++;
     if (c>7) c = 0;
    }
   else
   {
    c--;
    if (c<0) c = 7;
   }
  }
 }
 n2 = SUM232;

 // вывод результата в файл
  char sym_rez;
  for (int q=0; q<=3; q++)
  {
   sym_rez = *((byte *)&n1+q);
   fprintf(f_end, "%c", sym_rez);
  }
  for (int q=0; q<=3; q++)
  {
   sym_rez = *((byte *)&n2+q);
   fprintf(f_end, "%c", sym_rez);
  }
 }
 fclose (f_begin);
 fclose (f_end);
}

//---------------------------------------------------------------------------
int main()
{
 // выбираем шифрование или расшифрование
 int rezhim = 0;
 do
 {
  printf("Выберите режим работы:\nШифрование - 1\nРасшифрование - 2\n");
   scanf("%d", &rezhim);
 } while ((rezhim!=1)&&(rezhim!=2)); // повторяем до тех пор, пока не будет введено 1 или 

 // выбираем исходный и конечный файлы (слэш '\' в пути писать как '\\') 
 char open_str[50], save_str[50];
 printf("\nВведите путь до исходного файла\n");
 scanf("%s", &open_str);
 printf("\nВведите путь до файла, в который требуется записать результат\n");
 scanf("%s", &save_str);

 rpz(rezhim, open_str, save_str); // запускаем РПЗ
 return 0;
}
//---------------------------------------------------------------------------

Отметим достоинства и недостатки данного режима.

Достоинства:

  • Исключается влияние перекрытия шифра на стойкость шифрования.
  • Возможно расшифровать любой блок независимо от его местоположения в криптограмме.
  • Простота синхронизации.

Недостатки:

  • Статистика сообщения проникает в статистику криптограммы.
  • Одна ошибка типа «замена знака» в криптограмме ведет за собой полное разрушение блока при расшифровании.

Применение.

Данный режим применяется в остальных трех режимах ГОСТ 28147-89, в ГОСТ Р34.11-94 при выполнении перемешивающего преобразования, а также для обработки ключей.Применять его для зашифрования/расшифрования не рекомендуется, есть более прогрессивные режимы, речь о которых пойдет в следующих частях.
Варрелл @FTM
карма
63,7
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (19)

  • 0
    А лицензия на «код на с++» есть?
    • –1
      у меня?
  • 0
    #include <vcl.h>
    #include <stdio.h>
    #include <conio.h>


    n1 = *((unsigned long *)&N);

    *facepalm*

    • 0
      Упс, отправилось раньше времени, но, впрочем, неважно, зачем нам ваши лекции? Да ещё и с говнокодом?
      • –1
        Про мои лекции я уже писал, что если не нравится — их можно и не читать. Я просто ищу своего читателя. Человеку свойственно познавать новое, но как человек сможет узнать о чем-то новом, если он даже не знает о существовании этого «нового»? Многие здесь далеки от криптографии и не знают, что есть еще и наш русский ГОСТ, зато RSA, AES — все на слуху.
        • 0
          Всё это можно замечательно почитать и в гугле, при желании. Достаточно ссылок. -_- Ну а вы даже код оформить и переписать поленились, как тут можно говорить про качество и читателей? Халатность это.
          • –1
            Между прочим, код у меня оформлен, я не знаю, почему парсер отступы убрал.
            • –2
              Оформить код, это не только отступы поставить. -_- И вообще вот: dobrokot.nm.ru/WinnieColorizer.html
              • –1
                вместо тега code выставил pre. теперь все более-менее. как раскрасить код, могу и раскрасить?
        • 0
          Про русский ГОСТ знают чуть более чем все, это на всех курсах мало-мальски связанных с криптографией, читают.
      • –2
        А «говнокод» оптимизировать некогда, да и незачем. Если кто-то решит использовать его в своих проектах, то он работает, причем прекрасно. А так всегда можно «допилить». В мире нет ничего идеального, увы.
        • +1
          Говнокод здесь можно писать без кавычек. Ну и такой код просто неуважение к читателю. Вы статью для чего пишите то тогда, если даже код влом оформить?
          • –1
            я не пойму, почему мне влом, если парсер все украшательства убирает?
            я рад еще, что он не в одну строчку пишет все…
    • 0
      void main()


      ab = *((byte *)&SUM232+q);


      f = fopen (opener,«rb»);
      ff = fopen (saver,«wb»);


      do
      {
      printf(«Viberite rezhim raboti:\nShifrovanie — 1\nRashifrovanie — 2\n»);
      scanf("%d", &rezhim);
      } while ((rezhim!=1)&&(rezhim!=2)); // повторяем до тех пор, пока не будет введено 1 или


      Кажется, пора мыть глаза с мылом.
      • 0
        а что, собственно говоря, не так?
        • 0
          если это c++, то должен быть:

          int main()

          во второй цитате нечто непонятное с какими-то byte и без комментариев и вообще приведения в c++ так делать не стоит.

          в третьем случае фантастические названия переменных.

          в четвёртом ну просто без комментариев, это супер. как раз так на лабах в первом курсе делают и транслит ещё, да -_-
          • 0
            чуть поправил. спасибо за замечания.
  • 0
    в одной книге Шнайера код ГОСТа как-то немножко получше выглядит.
  • 0
    Скажите, а насколько устойчивость у этого алгоритма зависит от таблицы перекодировки? Достаточно ли менять только ключ или желательно и таблицу перекодировки тоже каждый раз генерировать заново, напр на основе ключа и другого алгоритма шифрования?

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