Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Тема закрыта

Реализация алгоритма шифрования ГОСТ 28147-89 на VHDL

Пн июн 18, 2012 12:13:25

Изначально меня послали на этот сайт "здесь много материала". угу. в разделе обучалки целых две статьи по плисам. и обе немного не по тому что мне надо.
ладно. залез на форум. там много-много тем, которые практически мгновенно тонут. по этому углубился (углупился) в книги. на русском языке - там всё совсем грустно. по организации форума - не думаю что место совсем новичку писать, и тем более не в этой теме.


ГОСТ 28147-89, как и любой другой симметричный алгоритм шифрования, состоит из простых операций.
Это – сдвиг, замена, суммирование по модулю 2, суммирование по модулю 2^32, объявление и присваивание переменных.
Рассмотрим создание проекта, осуществляющего шифрование блоков сообщения согласно алгоритму ГОСТ 28147-89:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.ALL;
Это понадобится для реализации функции сдвига, а также других функций.

Объявление переменных:
entity gost is
port
(
N1 : in std_logic_vector(31 downto 0)
N2 : in std_logic_vector(31 downto 0) ;
N1_out : out std_logic_vector(31 downto 0);
N2_out : out std_logic_vector(31 downto 0)
);
end gost;
std_logic_vector – это массив переменных.
(31 downto 0) – это размер массива.

Мы только что объявили, что у нас 2 входных порта N1 и N2 и два выходных порта N1_out и N2_out.
Согласно ГОСТ 28147-89 – с внешним миром сообщаются 2 порта, которые работают и на вход и на выход одновременно. Но при этом индикации о том, что в конкретный момент данных в этом буфере нет. Поэтому возможны ситуации, когда на одном и том же порту в разные моменты времени будут исходные данные, промежуточный результат или уже зашифрованные данные. Программа постоянно читает данные с этого порта, передает их дальше и происходит зацикливание. Чтобы избежать ситуации бесконечного цикла в программе были введены раздельные порты для входных и выходных данных

внутри - используется unsigned вместо std_logic_vector - так как для некоторых операций типа сдвига и сложения - требуют именно этого типа переменных. наружу отдается std_logic_vector

Переменные:
ключи шифрования

Signal X0 : unsigned(31 downto 0) := "01001111010010100001110010101010";
Signal X1 : unsigned(31 downto 0) := "01001111010010100001110010101010";
Signal X2 : unsigned(31 downto 0) := "01001111010010100001110010101010";
Signal X3 : unsigned(31 downto 0) := "01001111010010100001110010101010";
Signal X4 : unsigned(31 downto 0) := "01001111010010100001110010101010";
Signal X5 : unsigned(31 downto 0) := "01001111010010100001110010101010";
Signal X6 : unsigned(31 downto 0) := "01001111010010100001110010101010";
Signal X7 : unsigned(31 downto 0) := "01001111010010100001110010101010";

ключи шифрования пока задаются явно, и одинаковые, но их можно менять, не суть в них.

Согласно ГОСТ 28147-89– 256 бит ключа разбивается на 8 частей по 32 бита, которые храниться в X0-X7.




Signal SM1 : unsigned(31 downto 0);
Signal SM2 : unsigned(31 downto 0);
Это два сумматора. Один работает по модулю 2, другой по модулю 2^32

Signal K : unsigned(31 downto 0);
Signal R : unsigned(31 downto 0);
K – Это модуль, где происходит операция замены текста по заранее определенным таблицам.
R – узел смещения.

Текст цикла шифрования.

sm1 <= n1 and x0;
Сложение по модулю 2^32. В этой операции мы складываем половину зашифрованных данных с первым ключом.

Затем данные подаются на таблицу замен.

process
begin
case sm1(31 downto 28) is
when "0000" => k(31 downto 28) <= "0100"; -- 0 > 4
when "0001" => k(31 downto 28) <= "1010"; -- 1 > A
when "0010" => k(31 downto 28) <= "1001"; -- 2 > 9
when "0011" => k(31 downto 28) <= "0011"; -- 3 > 2
when "0100" => k(31 downto 28) <= "1101"; -- 4 > D
when "0101" => k(31 downto 28) <= "1000"; -- 5 > 8
when "0110" => k(31 downto 28) <= "0000"; -- 6 > 0
when "0111" => k(31 downto 28) <= "1110"; -- 7 > E
when "1000" => k(31 downto 28) <= "0110"; -- 8 > 6
when "1001" => k(31 downto 28) <= "1011"; -- 9 > B
when "1010" => k(31 downto 28) <= "0001"; -- A > 1
when "1011" => k(31 downto 28) <= "1100"; -- B > C
when "1100" => k(31 downto 28) <= "0110"; -- C > 7
when "1101" => k(31 downto 28) <= "1111"; -- D > F
when "1110" => k(31 downto 28) <= "0101"; -- E > 5
when "1111" => k(31 downto 28) <= "1001"; -- F > 9
when others => sm1(31 downto 28) <= "1001";
end case;
end process;


Данные разбиваются на 8 кусков по 4 бита. Каждый кусок заменяется согласно таблице, использующейся в криптографических приложениях ЦБ РФ. Это увеличивает стойкость шифрования к некоторым видам атак.
Цикл case должен быть в рамках цикла process begin …. end process;
Таковы особенности данного языка программирования.
когда вся реализация будет - процесс распространить на весь алгорим.

r <= k(10 downto 0) & k(31 downto 11);
Перемешивание данных. Данная операция затрудняет криптоанализ.

sm2 <= r xor n2;
Остается последний сумматор sm2 в котором складывается часть открытого текста с уже зашифрованными данными.

n2_out <= n1;
n1_out <= sm2;

end gost;
На этом текст цикла зашифрования заканчивается. Цикл расшифрования идентичен циклу шифрования.
Режим имитовставки идентичен режиму шифрования, за исключением количества циклов, и того что блоки данных суммируются по модулю 2^32.

до сюда - это текст диплома.
в ходе отладки были произведены некоторые изменения.
1. таблицы замен были вынесены в отдельную переменную. делать сиё просто, по этому не буду описывать как. хотя если надо..

2. введены задержки.
wait for 10 ns;

почему не используются отдельные функции? типа передать раздачу ключей наружу и таблицу замен тоже наружу? это еще + сколько то то там тактов на каждый раунд. что приведет к потере скорости раза в полтора. а нам нужна очень быстрая реализация.

после каждой комманды которая требует выполнения предыдущей. на один раунд шифрования где то 6-7 тактов уходит. на 16 циклов шифрования одного блока данных (имитовставка) - 99. в принципе можно оптимизировать на один так, но лень, а так же не совсем верно идеологически.
т.е. скорость получалась до 64 мегабит в секунду в режиме имитовставки на частоте в 100 мегагерц (плата позволяет до 324 мегагерц работать) и 32 мегабита в режиме простого шифрования/расшифрования.
т.е. разогнав плату до 300 мегагерц мы получаем 96 мегабит в режиме шифрования/расшифрования простой замены - что вполне достаточно для большинства сетей. в принципе для режима простой замены можно провести распарралеливание обработки данных. но это скорее тема отдельного исследования.

проблема с задержками - 1. они не везде документированы. т.е. до задержек я дошел изучая англоязычную литературу на тему. русскоязычной с удобными программами - нет.
англоязычной для совсем нубов - есть.
т.е. у меня было две книги на английском языке - задача + пример кода для решения этой задачи. к сожалению в какой то момент там тоже начались косяки с работоспособностью программ. т.е. там еще требуется какие то телодвижения которых прямо не указано в тексте.
2. с задержками невозможно проверять синтаксис. таковы особенности реализации среды программирования. т.е. карту реализации, занятость кристалла и прочее не проверить. оно не работает в таком виде в режиме отладки синтаксиса. просто закомментировать задержки тоже не вариант - он будет ругаться на использование одних и тех же переменных в разных местах. что не есть хорошо.
я фактически полдня убил на то что бы поискать альтернативную их реализацию. знай я сразу что их так реализовывать - недели две-три точно бы сэкономил.

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

тест программы не прикладывается -по этому позже выложу её.

Re: Реализация алгоритма шифрования ГОСТ 28147-89 на VHDL

Пн июн 18, 2012 14:05:57

2. введены задержки.
wait for 10 ns;

Если речь не о симуляции, а о выполняемых действиях в ПЛИС, то операция wait не поддерживается. Лучше для этой цели назначить отдельный сигнал тактирования.
Кстати о тактировании. Проект Ваш несинхронный, а ПЛИС такое не любят
PS Дополню
По тактированию (альтера насколько понял) лучше добавить в процессе после BEGIN
if(rising_edge(clk)) then
бла бла бла --Ваш код
end if;

По сигналам. Ключи лучше объявлять через constant - это даст возможность не меняться ключам при исполнении синтеза.

Re: Реализация алгоритма шифрования ГОСТ 28147-89 на VHDL

Пн июн 18, 2012 14:19:01

из мануалов у меня не получалось сделать реакцию на изменение фронта сигнала. по этому реализовывал как знаю. точнее на то что вычитал по
тактируемого сигнала опять же не получилось сделать. заготовка была, но так что бы было понятно объяснено как это делать - не видел. по этому написал как умею.
т.е. было почти написана программа которая и тактируема и прочее. но - не работала в симуляции нормально. "точка входа не прописана".
после этого я переписал как я изначально писал - последовательным выполнением всех действий которые описаны в госте. пусть не оптимально с точки зрения размера кода и с точки зрения возможностей отладки кода - но оно выдавало результат. что интереснее чем правильно написанная программа, которая не выдает результат.

Re: Реализация алгоритма шифрования ГОСТ 28147-89 на VHDL

Пн июн 18, 2012 14:24:00

Meteor писал(а):
PS Дополню
По тактированию (альтера насколько понял) лучше добавить в процессе после BEGIN
if(rising_edge(clk)) then
бла бла бла --Ваш код
end if;

По сигналам. Ключи лучше объявлять через constant - это даст возможность не меняться ключам при исполнении синтеза.

Xilinx
с ифами у меня была попытка реалиазции. проблема в том что оно криво работало. точнее постоянно ругалось на различные непонятные для меня вещи.

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

Re: Реализация алгоритма шифрования ГОСТ 28147-89 на VHDL

Пн июн 18, 2012 14:31:54

У Xilinx объявляются
Код:
if(clk'event and clk='1')then

Может у Вас были "незакрытые" части? (когда число if не равно числу end if)
Если ключи изменяемые - задайте отдельный порт для их ввода - это значительно лучше чем объявлять их сигналом. (ну на мой взгляд)
Для примера выложу как пишу код под ксилых (не кодировщик)
Код:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity Matrix is
    Port ( clk : in  STD_LOGIC;
           strt : in  STD_LOGIC;
           ADCDI : in  STD_LOGIC_VECTOR (9 downto 0);
           Y_strt,Y_clk,X_str, AD_clk,Sys_clk,clk12:out STD_LOGIC;
           ADCDO : out  STD_LOGIC_VECTOR (7 downto 0);
           Pix_x, Pix_y:out std_logic_vector(10 downto 0)
           );
end Matrix;

architecture Behavioral of Matrix is

signal start:std_logic;
signal clock1:std_logic:='0';
signal clock2:std_logic:='1';
signal ram_wr:std_logic:='1';
signal data:std_logic_vector(7 downto 0);
signal var_col:std_logic_vector(10 downto 0);
signal var_str:std_logic_vector(10 downto 0);


begin
----------
clocking:process(clk)--formire clocks ADC
   variable cnt:std_logic_vector(1 downto 0);
   variable cnt_str:std_logic_vector(10 downto 0);
   variable cnt_col:std_logic_vector(10 downto 0);
   variable cnt_point:std_logic_vector(20 downto 0);
   variable bf_strt:std_logic_vector(3 downto 0):="0000";
   variable bf_clk1,bf_clk2:std_logic_vector(3 downto 0);
   
   variable strb:std_logic;
   variable ram_wr_en:std_logic:='0';
   variable AD_data:std_logic_vector(9 downto 0);
   constant step1:std_logic_vector(1 downto 0):="01";
   constant ris:std_logic_vector(1 downto 0):="10";
   constant fal:std_logic_vector(1 downto 0):="00";
   constant step2:std_logic_vector(10 downto 0):="00000000001";
   constant zero:std_logic_vector(10 downto 0):="00000000000";
   constant x_stop:std_logic_vector(10 downto 0):="10100000001";
   constant y_stop:std_logic_vector(10 downto 0):="10000000000";
   constant clk_edge_f:std_logic_vector(3 downto 0):="1001";
   constant clk_edge_r:std_logic_vector(3 downto 0):="0110";
   constant Y_clk_f:std_logic_vector(10 downto 0):="00000000001";
   constant Y_clk_l:std_logic_vector(10 downto 0):="00000000010";
   constant Y_strt_l:std_logic_vector(10 downto 0):="00000000011";
   constant x_pause:std_logic_vector(10 downto 0):="10100101101";
   begin
   if(clk'event and clk='1')then
      cnt:=cnt+step1;
      if(cnt=ris)then
         clock1<='1';
         clock2<='0';
      elsif(cnt=fal)then
         clock1<='0';
         clock2<='1';
      end if;
      bf_strt(3 downto 1):=bf_strt(2 downto 0);
      bf_strt(0):=strt;--Detection start system
      bf_clk1(3 downto 1):=bf_clk1(2 downto 0);
      bf_clk1(0):=clock1;
      bf_clk2(3 downto 1):=bf_clk2(2 downto 0);
      bf_clk2(0):=clock2;
      strb:=std_logic(bf_clk1(3)and(bf_clk1(2)));
      AD_clk<=std_logic(bf_clk1(3));
      Sys_clk<=std_logic(bf_clk2(3));
      clk12<=bf_clk2(2);
                  case conv_integer(cnt_str) is
               when 0 => if (conv_integer(cnt_col)=0) then
                           Y_strt<='1';
                           cnt_col:=cnt_col+1;
                        elsif(conv_integer(cnt_col)=1) then
                           Y_clk<='1';
                           cnt_col:=cnt_col+1;
                        elsif(conv_integer(cnt_col)=2)then
                           Y_clk<='0';
                           cnt_col:=cnt_col+1;
                        elsif(conv_integer(cnt_col)=3)then
                           Y_strt<='0';
                           cnt_col:=cnt_col+1;
                        elsif(conv_integer(cnt_col)=1325)then
                           cnt_str:=cnt_str+1;                        
                           cnt_col:=zero;
                        else
                           cnt_col:=cnt_col+1;
                        end if;
               when others => if(conv_integer(cnt_col)=1)then
                                 Y_clk<='1';
                                 cnt_col:=cnt_col+1;
                              elsif(conv_integer(cnt_col)=2)then
                                 Y_clk<='0';
                                 X_str<='1';
                                 --var_col<=var_col+1;
                              elsif(conv_integer(cnt_col)=1325)then
                                 cnt_col:=zero;
                                 X_str<='0';
                              else
                                 cnt_col:=cnt_col+1;
                              end if;
            end case;
               Pix_x<=cnt_col;
               Pix_y<=cnt_str;
            ADCDO<=AD_data(9 downto 2);
   end if;
end process;
----------
end Behavioral;

Re: Реализация алгоритма шифрования ГОСТ 28147-89 на VHDL

Пн июн 18, 2012 14:47:51

mahury писал(а):Данные разбиваются на 8 кусков по 4 бита. Каждый кусок заменяется согласно таблице, использующейся в криптографических приложениях ЦБ РФ. Это увеличивает стойкость шифрования к некоторым видам атак.

Узлы замены лучше сделать загружаемыми. Вещь, в общем случае, не единая и не постоянная.

На этом текст цикла зашифрования заканчивается. Цикл расшифрования идентичен циклу шифрования.

Рекомендую уточнить свои сведения. Особенно о последних 8 раундах.

Re: Реализация алгоритма шифрования ГОСТ 28147-89 на VHDL

Пн июн 18, 2012 14:57:11

Satyr писал(а):
mahury писал(а):Данные разбиваются на 8 кусков по 4 бита. Каждый кусок заменяется согласно таблице, использующейся в криптографических приложениях ЦБ РФ. Это увеличивает стойкость шифрования к некоторым видам атак.

Узлы замены лучше сделать загружаемыми. Вещь, в общем случае, не единая и не постоянная.

На этом текст цикла зашифрования заканчивается. Цикл расшифрования идентичен циклу шифрования.

Рекомендую уточнить свои сведения. Особенно о последних 8 раундах.

узлы замены - в самой программе выведены в отдельные переменные.
сделать их подаваемыми снаружи - реально, но в задачу не входило.


раунды - одинаковые. отличается лишь порядок использования ключей.
по крайней мере так согласно тому тексту госта который я достал.

Re: Реализация алгоритма шифрования ГОСТ 28147-89 на VHDL

Пн июн 18, 2012 15:01:20

Meteor писал(а):У Xilinx объявляются
Код:
if(clk'event and clk='1')then

Может у Вас были "незакрытые" части? (когда число if не равно числу end if)
Если ключи изменяемые - задайте отдельный порт для их ввода - это значительно лучше чем объявлять их сигналом. (ну на мой взгляд)

не. там специально один иф использовали. тупо по тому как написано в образцовой программе пишу - не работает. по этому и не стал использовать.
Тема закрыта