ГОСТ 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 при выполнении перемешивающего преобразования, а также для обработки ключей.Применять его для зашифрования/расшифрования не рекомендуется, есть более прогрессивные режимы, речь о которых пойдет в следующих частях.
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 19
    • 0
      А лицензия на «код на с++» есть?
    • 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
                Скажите, а насколько устойчивость у этого алгоритма зависит от таблицы перекодировки? Достаточно ли менять только ключ или желательно и таблицу перекодировки тоже каждый раз генерировать заново, напр на основе ключа и другого алгоритма шифрования?

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

                Интересные публикации