CAST-128: Реализация на C++

CAST-128: Реализация на C++

Алгоритм шифрования CAST-128 (также CAST5) был разработан Карлайлом Адамсом и Стаффордом Тавересом в 1996 году. Он представляет собой блочный алгоритм симметричного шифрования (один ключ как для шифровки, так и для расшифровки сообщений) с размером блока 64 бит и ключом 40-128 бит (с шагом 8 бит).

РЕКОМЕНДУЕМ: Основные структуры данных

CAST-128: Начало реализации на C++

Со словесным описанием работы алгоритма CAST-128 можно ознакомиться по этому адресу: tools.ietf.org/html/rfc2144 (на англ. языке). Мы разберемся с особенностями работы алгоритма по ходу его реализации на C++.

Начнем с заголовочного файла cast128.h:

Сначала мы определяем типы Key и Message. Они представляют собой массивы, состоящие из 4 и 2 (соответственно) 32-битных беззнаковых целых чисел quint32.

Как для шифрования ( encrypt()), так и для дешифрования ( decrypt()) алгоритм CAST-128 принимает на вход ключ key и кодируемое сообщение msg. В рассматриваемой реализации мы предполагаем, что ключ всегда имеет длину 128 бит (для ключей меньшего размера изменения в алгоритме окажутся минимальными).

Принцип работы функций encrypt() и decrypt() очень похож, но есть и отличие: для расшифровки нужно делать все то же самое, что и для шифрования, но только в обратном порядке. Для этого мы создаем вспомогательную функцию run(), которой можно передать дополнительный флаг reverse ( false для шифрования и true для дешифровки).

Предполагается, что шифрование и дешифрование осуществляется «на месте». Т.е. сообщения msg кодируются без копирования.

Обратите внимание, что мы заготовили восемь массивов (S-блоков) S1, S2, …, S8, состоящих из 256 беззнаковых целых чисел quint32. Они предустановлены и приводятся в приложении из документа, описывающего работу алгоритма. Рассмотрим их форму на примере S1:

До применения S-блоков мы скоро дойдем.

CAST-128: Ожидаемый результат работы

До перехода к реализации напишем два небольших теста (см. TDD) на основе данных для самопроверки из знакомого нам документа.

Файл main.cpp:

В Тесте 1 используется ключ 0x01234567, 0x12345678, 0x23456789, 0x3456789A и сообщение 0x01234567, 0x89ABCDEF. Сначала мы кодируем сообщение с помощью encrypt(). Затем полученное сообщение дешифруется с помощью decrypt(). В результате таких манипуляций мы должны вновь получить исходное сообщение:

Для Теста 2 используется более сложная схема шифрования. Два ключа a и b шифруются по очереди друг другом один миллион раз. В результате получается:

Этот результат соответствует данным для проверки CAST-128.

CAST-128: Реализация на C++

Файл cast128.cpp:

Работа алгоритма начинается с формирования 32 ключей развертки K. Для их построения используется копия x ключа key и временный ключ z. Заполнение ключей развертки проходит в два цикла с использованием S-блоков S5, S6, S7 и S8. При этом задействована вспомогательная функция g() для извлечения i-го байта из ключа key:

Реализация функции g() использует карту преобразований K_MAP из-за порядка хранения байтов в памяти (алгоритм CAST-128 требует порядок от старшего к младшему, а на архитектуре x86 байты хранятся от младшего к старшему). Замечание: порядок может меняться в зависимости от платформы и реализации компилятора.

Если key = { 0x01234567, 0x12345678, 0x23456789, 0x3456789A }, то g( key, 0 ) == 0x01 (без K_MAP — 0x67), а g( key, 6 ) == 0x56 (без K_MAP0x34) и т.д.

Кодирование с помощью 128-битного ключа осуществляется в 16 раундов. Для каждого i-го раунда используются подключи маскировки Kmi и подключи перестановки Kri. При шифровании Kmi = K[ i ] и Kri = K[ 16 + i ] & 0x1F (5 наименее значащих битов), а при расшифровке: Kmi = K[ 15 - i ] и Kri = K[ 31 - i ] & 0x1F.

Исходное сообщение разбивается на левую L и правую R части. Первичная инициализация осуществляется следующим образом: L[ 0 ] = msg[ 0 ] и R[ 0 ] = msg[ 1 ]. Далее над этими половинами производятся манипуляции. При этом в разных раундах используются разные функции преобразований:

Здесь используются вспомогательные функции cyclicShift() — циклический побитовый сдвиг влево; sumMod2_32() — сумма по модулю 2^32; subtractMod2_32() — вычитание по модулю 2^32 и splitI() — функция для разбиения 32-битного числа на четыре 8-битных в порядке от старшего байта Ia к младшему Id.

Обратите внимание, что для вычисления f мы используем S-блоки S1, S2, S3 и S4.

Результатом работы i-го раунда становится: L[ i + 1 ] = R[ i ] и R[ i + 1 ] = L[ i ] ^ f. Окончательно закодированное сообщение определяется в виде msg[ 0 ] = R[ 16 ] и msg[ 1 ] = L[ 16 ].

Понравилась статья? Поделиться с друзьями:
Добавить комментарий