Шифр "Кузнечик", в отличие от своих предшественников, имеет в основе не сеть Фейстеля, а SP-сеть. Использование SP-сети позволяет выполнить преобразования над всем входным блоком целиком, а не только над его половиной.
Процесс шифрования состоит из нескольких раундов, каждый из которых включает в себя несколько преобразований, а именно три: сложение по модулю 2 (xor) с раундовым ключом, замена с помощью блоков подстановок и линейное преобразование.
Раундовые ключи, их десять, вырабатываются на основе 128-битного мастер-ключа. Первые два ключа получаются путем разбиения мастер-ключа пополам, а последующие восемь при помощи восьми раундов сети Фейстеля. В каждом раунде осуществляются: xor ключа с раундовой константой, преобразование с помощью блока подстановок, линейное преобразование. Раундовую константу получаем из применения к номеру раунда линейного преобразования.
Алгоритм шифрования:
Где — обозначает шифрование блока открытого текста на десяти раундовых ключах взятых по порядку;
— операция xor между раундовым ключом и блоком текста;
S — преобразование с помощью блоков подстановок;
L — линейное преобразование;
(a) — открытый текст;
Результат шифрования – шифртекст.
Алгоритм дешифрования:
— операция xor между раундовым ключом и блоком текста;
— преобразование с помощью блоков подстановок, обратное преобразованию ;
— линейное преобразование, обратное преобразованию ;
(b) — шифртекст;
Результат дешифрования – открытый текст.
Один раунд шифрования можно представить следующим образом (Рис.1):
Входной блок
Ki
a3
a4
a5
a6
a7
a8
a9
a10
a11
a12
a13
a14
a15
a0
a1
a2
π
π
π
π
π
π
π
π
π
π
π
π
π
π
π
π
a15
a14
a0
a1
a2
a3
a4
a5
a6
a7
a8
a9
a10
a11
a12
a13
Выходной блок
a2
a3
a4
a5
a6
a7
a8
a9
a10
a11
a12
a13
a14
a15
a0
a1
ℓ
a1
a2
a3
a4
a5
a6
a7
a8
a9
a10
a11
a12
a13
a14
a15
a0
Линейное преобразование (16 раундов)
Преобразование в подстановочном блоке
Рисунок 1. Один раунд шифрования
Программная реализация алгоритма
Программа написана на языке программирования С/С++. Блок открытого текста размером 128 бит представлен в виде двух переменных типа unsigned long long, каждая из которых может хранить данные размером 64 бита. 256-битный мастер ключ представлен четырьмя переменными типа unsigned long long. Все ключи, мастер-ключ и выработанные программой ключи, хранятся в массиве, состоящем из десяти структур struct funcKeys, каждая из них содержит две переменные, в которых хранятся 64-битные части ключа: unsigned long long key1 и unsigned long long key2. Раундовые константы, их 32, так же помещены в массив структур struct N_const, содержащий две половинки каждой константы и состоящий из 32-х элементов. В основе функций, которые содержат в себе линейное преобразование и преобразование с помощью блока подстановки лежат операции битовых сдвигов.
Функция шифрования принимает открытый текст и после выполнения всех раундов шифрования выводит блок зашифрованного текста.
int encrypt(unsigned long long* text1, unsigned long long* text2, unsigned long long* cipher1, unsigned long long* cipher2)
Рассмотрим функцию шифрования подробнее.
Внутри функции шифрования первым делом происходит вызов функции, которая отвечает за выработку ключа.
func_key_deploy(&master_key1_1, &master_key1_2, &master_key2_1, &master_key2_2);
Внутри функции выработки ключа мы записываем половинки мастер-ключа в структуру, созданную для хранения ключей, как K1 и K2. На основе уже существующих K1 и K2 производится 8 раундов сети Фейстеля, на основе которой вырабатываются следующая пара ключей K3 и K4. Остальные три пары ключей вырабатываются по аналогии: K5 и K6, K7 и K8, K9 и K10.
Разбиение мастер ключа на правую и левую части.
unsigned long long KR1 = array_keys[0].key1;
unsigned long long KR2 = array_keys[0].key2;
unsigned long long KL1 = array_keys[1].key1;
unsigned long long KL2 = array_keys[1].key2;
Сначала указываем, что будет 4 итерации внешнего цикла для выработки 4-х пар ключей.
for (l=0; l