Данный проект представляет собой реализацию российского блочного шифра <<Кузнечик>>, утверждённого в ГОСТ Р 34.12-2015.
Данный проект чётко следует документации шифра, описанной в стандарте (ГОСТе) и реализует функции согласно ей. Блочный шифр вообще - это функция, осуществляющая отображение блоков (битовых строк фиксированной длины) в перестановку на этом множестве блоков. Блочный шифр параметризуется ключом. <<Кузнечик>> работает с блоками длины 128 бит и ключом длины 256 бит.
В проекте реализованы функции шифрования и дешифрования одного конкретного блока из 128 бит (при заданном ключе). Режимы шифрования текста (типо CBC или других), то есть разбиение его на блоки, их шифрование в правильном и безопасном порядке, не реализовано. В проекте только сам блочный шифр.
Также для функции шифрования реализована более быстрая версия, использующая некоторую алгоритмическую оптимизацию, не указанную в стандарте. Аналогично можно реализовать и быструю версию дешифрования, но в проекте этого нет.
Код проекта снабжён большим количеством комментариев, в которых подробно описывается, что и как делается, в том числе описана оптимизация шифрования.
-
В файле
kuznechik.c
реализованы функции, необходимые для работы блочного шифра - всё, что было описано в стандарте. -
В файле
test_correct.c
находится код для тестирования функций блочного шифра. Все примеры, на которых производится тестирование, тоже взяты из стандарта. -
В файле
test_speed.c
находится код для измерения скорости работы функций шифрования (обычной, написанной по стандарту и оптимизированной). -
В файле
main.c
производится лишь запуск тестов. При необходимости его можно поменять, обеспечив требуемую функциональность.
Вообще для использования кода (то есть для шифрования и дешифрования блоков) необходимо перед шифрованием и дешифрованием сделать следующее:
- Сначала запустить функцию
compute_consts()
, которая посчитает необходимы константы для работы алгоритма. - Затем запустить функцию
expand_key(master_key)
, куда в качестве параметра передаётся мастер-ключ - основной ключ блочного шифра (из него потом генерирутся другие ключи внутри алгоритма) длины 256 бит (это и есть основной секрет, который нельзя разглашать для работы шифрования) - то есть фактически массив из 256 бит данных. - Предыдущих двух функций достаточно для обычной работы шифра, но если нужно воспользоваться функцией быстрого шифрования, то перед этим нужно запустить функцию
compute_values_L()
, которая посчитает еще значения. - Для шифрования используется функция
kuznechik_encrypt()
, для дешифрования - функцияkuznechik_decrypt()
. В каждую из них передаются указатели на память, откуда считать блок, и куда записать результат (шифрования или дешифрования).
Для лучшего понимания, стоит обратить внимание на код в test_correct.c
, где все эти функции запускаются (Сами функции из kuznechik.c
).
Сам блок, который занимает 128 бит, хранится в виде массива из 16 элементов. Каждый элемент типа uint8_t и занимает ровно 1 байт. Таким образом, блок в коде представляется массивом из 16 байтов.
Для считывания примеров, записанных в документации в виде шестандцатиричных строк вида ffeeddccbbaa99881122334455667700
, используется функция hexstring_to_array()
из файла test_correct.c
. Для тех, кто не знает, как это работает: такая строка кодирует 128 бит данных, а именно каждый символ строки - шестнадцатиричная цифра, каждые две цифры образуют двузначное шестнадцатиричное число, а оно принимает значения от 0 до 255. То есть две шестнадцатиричные цифры кодируют один байт. Таким образом, данная строка - это 16 байт = 128 бит. Нужно обратить внимание, что в российских стандартах примеры пишутся со старшего байта: то есть в приведённом примере строка имеет старший байт = ff
(= 255) и младший байт, равный 00
(= 0).
Оптимизация шифрования заключается в том, чтобы, воспользовавшись линейностью одного из отображений внутри блочного шифра, сделать предподсчёт: посчитать это линейное преобразование на блоках, где только один байт не равен 0. Так как байт принимает значения от 0 до 255, а всего байтов в блоке 16, то получается небольшая матрица заранее посчитанных значений. А далее, при применении, можно просто брать уже посчитанные значения из таблицы, и складывать их (пользуясь линейностью), получая результат преобразования на вем входе.
В проекте есть Makefile, который запускается командой make
(из той же папки, где находится весь проект) и автоматически компилирует все исходные файлы.
После сброки (компиляции) проекта появляется исполняемый файл main
, запустив который ./main
, должно вывестись такое сообщение:
Начинается тестирование корректности кода...
--> Тестирование преобразований S, R, L завершено
--> Тестирование ключей завершено
--> Тесты шифрования завершены
--> Тесты ускоренного шифрования завершены
Тестирование корректности окончено. Если не было выведено сообщений об ошибках, значит тесты пройдены верно!
Начинаем тестирование скорости...
Обычное шифрование:
--> Зашифровано 10000 блоков за 1.53 секунд
--> Скорость шифрования ~ 0.83 Мбит в секунду
Тест ускоренного шифрования:
--> Зашифровано 1000000 блоков за 1.18 секунд
--> Скорость шифрования ~ 108.02 Мбит в секунду
Первая часть вывода сообщает, что тесты на правильность работы функций пройдена успешно (то есть программа работает верно). Вторая часть сравнивает скорость шифрования обычным методом и ускоренным: разница очень большая.