Алгоритм шифрования CAST-128 (также CAST5) был разработан Карлайлом Адамсом и Стаффордом Тавересом в 1996 году. Он представляет собой блочный алгоритм симметричного шифрования (один ключ как для шифровки, так и для расшифровки сообщений) с размером блока 64 бит и ключом 40-128 бит (с шагом 8 бит).
РЕКОМЕНДУЕМ: Основные структуры данных
CAST-128: Начало реализации на C++
Со словесным описанием работы алгоритма CAST-128 можно ознакомиться по этому адресу: tools.ietf.org/html/rfc2144 (на англ. языке). Мы разберемся с особенностями работы алгоритма по ходу его реализации на C++.
Начнем с заголовочного файла cast128.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#ifndef CAST128_H #define CAST128_H #include <Qt> class CAST128 { public: enum { KEY_LEN = 128 / 32, MSG_LEN = 2 }; typedef quint32 Key[ KEY_LEN ]; typedef quint32 Message[ MSG_LEN ]; public: CAST128(); void encrypt( const Key key, Message msg ); void decrypt( const Key key, Message msg ); private: void run( const Key key, Message msg, bool reverse = false ); private: typedef quint32 SType[ 256 ]; static const SType S1; static const SType S2; static const SType S3; static const SType S4; static const SType S5; static const SType S6; static const SType S7; static const SType S8; }; #endif // 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
const CAST128::SType CAST128::S1 = { 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, 0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, 0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, 0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c, 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf }; |
До применения S-блоков мы скоро дойдем.
CAST-128: Ожидаемый результат работы
До перехода к реализации напишем два небольших теста (см. TDD) на основе данных для самопроверки из знакомого нам документа.
Файл main.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#include "cast128.h" #include <iostream> #include <iomanip> void showComponent( quint32 x ) { std::cout << std::hex << std::setw( 8 ) << std::setfill( '0' ) << std::uppercase << x << " "; } void showMessage( const CAST128::Message msg ) { for( int i = 0; i < CAST128::MSG_LEN; ++i ) { showComponent( msg[ i ] ); std::cout << " "; } std::cout << std::endl; } void showKey( const CAST128::Key key ) { for( int i = 0; i < CAST128::KEY_LEN; ++i ) { showComponent( key[ i ] ); std::cout << " "; } std::cout << std::endl; } int main() { CAST128 cast128; std::cout << "================ Test 1 ================" << std::endl; static const CAST128::Key KEY = { 0x01234567, 0x12345678, 0x23456789, 0x3456789A }; CAST128::Message msg = { 0x01234567, 0x89ABCDEF }; std::cout << " Msg before: "; showMessage( msg ); cast128.encrypt( KEY, msg ); std::cout << "Msg after encryption: "; showMessage( msg ); cast128.decrypt( KEY, msg ); std::cout << "Msg after decryption: "; showMessage( msg ); std::cout << std::endl; std::cout << "================ Test 2 ================" << std::endl; CAST128::Key a = { 0x01234567, 0x12345678, 0x23456789, 0x3456789A }; CAST128::Key b = { 0x01234567, 0x12345678, 0x23456789, 0x3456789A }; for( int i = 0; i < 1000000; ++i ) { cast128.encrypt( b, a ); cast128.encrypt( b, a + 2 ); cast128.encrypt( a, b ); cast128.encrypt( a, b + 2 ); } showKey( a ); showKey( b ); return 0; } |
В Тесте 1 используется ключ 0x01234567, 0x12345678, 0x23456789, 0x3456789A и сообщение 0x01234567, 0x89ABCDEF. Сначала мы кодируем сообщение с помощью encrypt(). Затем полученное сообщение дешифруется с помощью decrypt(). В результате таких манипуляций мы должны вновь получить исходное сообщение:
1 2 3 4 |
================ Test 1 ================ Msg before: 01234567 89ABCDEF Msg after encryption: 238B4FE5 847E44B2 Msg after decryption: 01234567 89ABCDEF |
Для Теста 2 используется более сложная схема шифрования. Два ключа a и b шифруются по очереди друг другом один миллион раз. В результате получается:
1 2 3 |
================ Test 2 ================ EEA9D0A2 49FD3BA6 B3436FB8 9D6DCA92 B2C95EB0 0C31AD71 80AC05B8 E83D696E |
Этот результат соответствует данным для проверки CAST-128.
CAST-128: Реализация на C++
Файл cast128.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
#include "cast128.h" #include <cstring> #include <iostream> #include <iomanip> static const int ROUND_COUNT = 16; static const quint64 MOD_2_32 = quint64( 2 ) << 31; static const quint8 K_MAP[ sizeof( CAST128::Key ) ] = { 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12 }; static quint8 g( const CAST128::Key key, quint8 i ) { return ( ( quint8* ) key )[ K_MAP[ i ] ]; } static void splitI( quint32 I, quint8* Ia, quint8* Ib, quint8* Ic, quint8* Id ) { *Ia = ( I >> 24 ) & 0xFF; *Ib = ( I >> 16 ) & 0xFF; *Ic = ( I >> 8 ) & 0xFF; *Id = ( I ) & 0xFF; } static quint32 sumMod2_32( quint32 a, quint32 b ) { return ( quint64( a ) + quint64( b ) ) % MOD_2_32; } static quint32 subtractMod2_32( quint32 a, quint32 b ) { if( b <= a ) { return a - b; } return ( MOD_2_32 + a ) - b; } static quint32 cyclicShift( quint32 x, quint8 shift ) { quint8 s = shift % 32; return ( x << s ) | ( x >> ( 32 - s ) ); } // **************************************************************************************************** // **************************************************************************************************** CAST128::CAST128() { } void CAST128::encrypt( const CAST128::Key key, CAST128::Message msg ) { run( key, msg ); } void CAST128::decrypt( const CAST128::Key key, CAST128::Message msg ) { run( key, msg, true ); } void CAST128::run( const CAST128::Key key, CAST128::Message msg, bool reverse ) { Key x = { 0 }; memcpy( x, key, sizeof( Key ) ); Key z = { 0 }; quint32 K[ 32 ] = { 0 }; for( int i = 0; i < 2; ++i ) { z[ 0 ] = x[ 0 ] ^ S5[ g( x, 0xD ) ] ^ S6[ g( x, 0xF ) ] ^ S7[ g( x, 0xC ) ] ^ S8[ g( x, 0xE ) ] ^ S7[ g( x, 0x8 ) ]; z[ 1 ] = x[ 2 ] ^ S5[ g( z, 0x0 ) ] ^ S6[ g( z, 0x2 ) ] ^ S7[ g( z, 0x1 ) ] ^ S8[ g( z, 0x3 ) ] ^ S8[ g( x, 0xA ) ]; z[ 2 ] = x[ 3 ] ^ S5[ g( z, 0x7 ) ] ^ S6[ g( z, 0x6 ) ] ^ S7[ g( z, 0x5 ) ] ^ S8[ g( z, 0x4 ) ] ^ S5[ g( x, 0x9 ) ]; z[ 3 ] = x[ 1 ] ^ S5[ g( z, 0xA ) ] ^ S6[ g( z, 0x9 ) ] ^ S7[ g( z, 0xB ) ] ^ S8[ g( z, 0x8 ) ] ^ S6[ g( x, 0xB ) ]; K[ 0 + i * 16 ] = S5[ g( z, 0x8 ) ] ^ S6[ g( z, 0x9 ) ] ^ S7[ g( z, 0x7 ) ] ^ S8[ g( z, 0x6 ) ] ^ S5[ g( z, 0x2 ) ]; K[ 1 + i * 16 ] = S5[ g( z, 0xA ) ] ^ S6[ g( z, 0xB ) ] ^ S7[ g( z, 0x5 ) ] ^ S8[ g( z, 0x4 ) ] ^ S6[ g( z, 0x6 ) ]; K[ 2 + i * 16 ] = S5[ g( z, 0xC ) ] ^ S6[ g( z, 0xD ) ] ^ S7[ g( z, 0x3 ) ] ^ S8[ g( z, 0x2 ) ] ^ S7[ g( z, 0x9 ) ]; K[ 3 + i * 16 ] = S5[ g( z, 0xE ) ] ^ S6[ g( z, 0xF ) ] ^ S7[ g( z, 0x1 ) ] ^ S8[ g( z, 0x0 ) ] ^ S8[ g( z, 0xC ) ]; x[ 0 ] = z[ 2 ] ^ S5[ g( z, 0x5 ) ] ^ S6[ g( z, 0x7 ) ] ^ S7[ g( z, 0x4 ) ] ^ S8[ g( z, 0x6 ) ] ^ S7[ g( z, 0x0 ) ]; x[ 1 ] = z[ 0 ] ^ S5[ g( x, 0x0 ) ] ^ S6[ g( x, 0x2 ) ] ^ S7[ g( x, 0x1 ) ] ^ S8[ g( x, 0x3 ) ] ^ S8[ g( z, 0x2 ) ]; x[ 2 ] = z[ 1 ] ^ S5[ g( x, 0x7 ) ] ^ S6[ g( x, 0x6 ) ] ^ S7[ g( x, 0x5 ) ] ^ S8[ g( x, 0x4 ) ] ^ S5[ g( z, 0x1 ) ]; x[ 3 ] = z[ 3 ] ^ S5[ g( x, 0xA ) ] ^ S6[ g( x, 0x9 ) ] ^ S7[ g( x, 0xB ) ] ^ S8[ g( x, 0x8 ) ] ^ S6[ g( z, 0x3 ) ]; K[ 4 + i * 16 ] = S5[ g( x, 0x3 ) ] ^ S6[ g( x, 0x2 ) ] ^ S7[ g( x, 0xC ) ] ^ S8[ g( x, 0xD ) ] ^ S5[ g( x, 0x8 ) ]; K[ 5 + i * 16 ] = S5[ g( x, 0x1 ) ] ^ S6[ g( x, 0x0 ) ] ^ S7[ g( x, 0xE ) ] ^ S8[ g( x, 0xF ) ] ^ S6[ g( x, 0xD ) ]; K[ 6 + i * 16 ] = S5[ g( x, 0x7 ) ] ^ S6[ g( x, 0x6 ) ] ^ S7[ g( x, 0x8 ) ] ^ S8[ g( x, 0x9 ) ] ^ S7[ g( x, 0x3 ) ]; K[ 7 + i * 16 ] = S5[ g( x, 0x5 ) ] ^ S6[ g( x, 0x4 ) ] ^ S7[ g( x, 0xA ) ] ^ S8[ g( x, 0xB ) ] ^ S8[ g( x, 0x7 ) ]; z[ 0 ] = x[ 0 ] ^ S5[ g( x, 0xD ) ] ^ S6[ g( x, 0xF ) ] ^ S7[ g( x, 0xC ) ] ^ S8[ g( x, 0xE ) ] ^ S7[ g( x, 0x8 ) ]; z[ 1 ] = x[ 2 ] ^ S5[ g( z, 0x0 ) ] ^ S6[ g( z, 0x2 ) ] ^ S7[ g( z, 0x1 ) ] ^ S8[ g( z, 0x3 ) ] ^ S8[ g( x, 0xA ) ]; z[ 2 ] = x[ 3 ] ^ S5[ g( z, 0x7 ) ] ^ S6[ g( z, 0x6 ) ] ^ S7[ g( z, 0x5 ) ] ^ S8[ g( z, 0x4 ) ] ^ S5[ g( x, 0x9 ) ]; z[ 3 ] = x[ 1 ] ^ S5[ g( z, 0xA ) ] ^ S6[ g( z, 0x9 ) ] ^ S7[ g( z, 0xB ) ] ^ S8[ g( z, 0x8 ) ] ^ S6[ g( x, 0xB ) ]; K[ 8 + i * 16 ] = S5[ g( z, 0x3 ) ] ^ S6[ g( z, 0x2 ) ] ^ S7[ g( z, 0xC ) ] ^ S8[ g( z, 0xD ) ] ^ S5[ g( z, 0x9 ) ]; K[ 9 + i * 16 ] = S5[ g( z, 0x1 ) ] ^ S6[ g( z, 0x0 ) ] ^ S7[ g( z, 0xE ) ] ^ S8[ g( z, 0xF ) ] ^ S6[ g( z, 0xC ) ]; K[ 10 + i * 16 ] = S5[ g( z, 0x7 ) ] ^ S6[ g( z, 0x6 ) ] ^ S7[ g( z, 0x8 ) ] ^ S8[ g( z, 0x9 ) ] ^ S7[ g( z, 0x2 ) ]; K[ 11 + i * 16 ] = S5[ g( z, 0x5 ) ] ^ S6[ g( z, 0x4 ) ] ^ S7[ g( z, 0xA ) ] ^ S8[ g( z, 0xB ) ] ^ S8[ g( z, 0x6 ) ]; x[ 0 ] = z[ 2 ] ^ S5[ g( z, 0x5 ) ] ^ S6[ g( z, 0x7 ) ] ^ S7[ g( z, 0x4 ) ] ^ S8[ g( z, 0x6 ) ] ^ S7[ g( z, 0x0 ) ]; x[ 1 ] = z[ 0 ] ^ S5[ g( x, 0x0 ) ] ^ S6[ g( x, 0x2 ) ] ^ S7[ g( x, 0x1 ) ] ^ S8[ g( x, 0x3 ) ] ^ S8[ g( z, 0x2 ) ]; x[ 2 ] = z[ 1 ] ^ S5[ g( x, 0x7 ) ] ^ S6[ g( x, 0x6 ) ] ^ S7[ g( x, 0x5 ) ] ^ S8[ g( x, 0x4 ) ] ^ S5[ g( z, 0x1 ) ]; x[ 3 ] = z[ 3 ] ^ S5[ g( x, 0xA ) ] ^ S6[ g( x, 0x9 ) ] ^ S7[ g( x, 0xB ) ] ^ S8[ g( x, 0x8 ) ] ^ S6[ g( z, 0x3 ) ]; K[ 12 + i * 16 ] = S5[ g( x, 0x8 ) ] ^ S6[ g( x, 0x9 ) ] ^ S7[ g( x, 0x7 ) ] ^ S8[ g( x, 0x6 ) ] ^ S5[ g( x, 0x3 ) ]; K[ 13 + i * 16 ] = S5[ g( x, 0xA ) ] ^ S6[ g( x, 0xB ) ] ^ S7[ g( x, 0x5 ) ] ^ S8[ g( x, 0x4 ) ] ^ S6[ g( x, 0x7 ) ]; K[ 14 + i * 16 ] = S5[ g( x, 0xC ) ] ^ S6[ g( x, 0xD ) ] ^ S7[ g( x, 0x3 ) ] ^ S8[ g( x, 0x2 ) ] ^ S7[ g( x, 0x8 ) ]; K[ 15 + i * 16 ] = S5[ g( x, 0xE ) ] ^ S6[ g( x, 0xF ) ] ^ S7[ g( x, 0x1 ) ] ^ S8[ g( x, 0x0 ) ] ^ S8[ g( x, 0xD ) ]; } quint32 L[ ROUND_COUNT + 1 ] = { 0 }; L[ 0 ] = msg[ 0 ]; quint32 R[ ROUND_COUNT + 1 ] = { 0 }; R[ 0 ] = msg[ 1 ]; for( int i = 0; i < ROUND_COUNT; ++i ) { int rIndex = reverse ? ( ROUND_COUNT - 1 - i ) : i; quint32 Kmi = K[ rIndex ]; quint8 Kri = K[ 16 + rIndex ] & 0x1F; quint32 I = 0; quint32 f = 0; quint8 Ia, Ib, Ic, Id; switch( rIndex % 3 ) { case 0: I = cyclicShift( sumMod2_32( Kmi, R[ i ] ), Kri ); splitI( I, &Ia, &Ib, &Ic, &Id ); f = sumMod2_32( subtractMod2_32( S1[ Ia ] ^ S2[ Ib ], S3[ Ic ] ), S4[ Id ] ); break; case 1: I = cyclicShift( Kmi ^ R[ i ], Kri ); splitI( I, &Ia, &Ib, &Ic, &Id ); f = sumMod2_32( subtractMod2_32( S1[ Ia ], S2[ Ib ] ), S3[ Ic ] ) ^ S4[ Id ]; break; case 2: I = cyclicShift( subtractMod2_32( Kmi, R[ i ] ), Kri ); splitI( I, &Ia, &Ib, &Ic, &Id ); f = subtractMod2_32( sumMod2_32( S1[ Ia ], S2[ Ib ] ) ^ S3[ Ic ], S4[ Id ] ); break; } L[ i + 1 ] = R[ i ]; R[ i + 1 ] = L[ i ] ^ f; } msg[ 0 ] = R[ ROUND_COUNT ]; msg[ 1 ] = L[ ROUND_COUNT ]; } // Для краткости определения S-блоков убраны const CAST128::SType CAST128::S1 = { ... }; const CAST128::SType CAST128::S2 = { ... }; const CAST128::SType CAST128::S3 = { ... }; const CAST128::SType CAST128::S4 = { ... }; const CAST128::SType CAST128::S5 = { ... }; const CAST128::SType CAST128::S6 = { ... }; const CAST128::SType CAST128::S7 = { ... }; const CAST128::SType CAST128::S8 = { ... }; |
Работа алгоритма начинается с формирования 32 ключей развертки K. Для их построения используется копия x ключа key и временный ключ z. Заполнение ключей развертки проходит в два цикла с использованием S-блоков S5, S6, S7 и S8. При этом задействована вспомогательная функция g() для извлечения i-го байта из ключа key:
1 2 3 4 5 6 7 8 9 10 |
static const quint8 K_MAP[ sizeof( CAST128::Key ) ] = { 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12 }; static quint8 g( const CAST128::Key key, quint8 i ) { return ( ( quint8* ) key )[ K_MAP[ i ] ]; } |
Реализация функции g() использует карту преобразований K_MAP из-за порядка хранения байтов в памяти (алгоритм CAST-128 требует порядок от старшего к младшему, а на архитектуре x86 байты хранятся от младшего к старшему). Замечание: порядок может меняться в зависимости от платформы и реализации компилятора.
Если key = { 0x01234567, 0x12345678, 0x23456789, 0x3456789A }, то g( key, 0 ) == 0x01 (без K_MAP — 0x67), а g( key, 6 ) == 0x56 (без K_MAP — 0x34) и т.д.
Кодирование с помощью 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 ]. Далее над этими половинами производятся манипуляции. При этом в разных раундах используются разные функции преобразований:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
switch( rIndex % 3 ) { case 0: I = cyclicShift( sumMod2_32( Kmi, R[ i ] ), Kri ); splitI( I, &Ia, &Ib, &Ic, &Id ); f = sumMod2_32( subtractMod2_32( S1[ Ia ] ^ S2[ Ib ], S3[ Ic ] ), S4[ Id ] ); break; case 1: I = cyclicShift( Kmi ^ R[ i ], Kri ); splitI( I, &Ia, &Ib, &Ic, &Id ); f = sumMod2_32( subtractMod2_32( S1[ Ia ], S2[ Ib ] ), S3[ Ic ] ) ^ S4[ Id ]; break; case 2: I = cyclicShift( subtractMod2_32( Kmi, R[ i ] ), Kri ); splitI( I, &Ia, &Ib, &Ic, &Id ); f = subtractMod2_32( sumMod2_32( S1[ Ia ], S2[ Ib ] ) ^ S3[ Ic ], S4[ Id ] ); break; } |
Здесь используются вспомогательные функции 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 ].