Алгоритм шифрования CAST-128 (также CAST5) был разработан Карлайлом Адамсом и Стаффордом Тавересом в 1996 году. Он представляет собой блочный алгоритм симметричного шифрования (один ключ как для шифровки, так и для расшифровки сообщений) с размером блока 64 бит и ключом 40-128 бит (с шагом 8 бит).
Со словесным описанием работы алгоритма CAST-128 можно ознакомиться по этому адресу: https://tools.ietf.org/html/rfc2144 (на англ. языке). Мы разберемся с особенностями работы алгоритма по ходу его реализации на C++.
Начнем с заголовочного файла cast128.h
:
#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
:
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-блоков мы скоро дойдем.
До перехода к реализации напишем два небольших теста (см. TDD) на основе данных для самопроверки из знакомого нам документа.
Файл main.cpp
:
#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()
. В результате таких манипуляций мы должны вновь получить исходное сообщение:
================ Test 1 ================
Msg before: 01234567 89ABCDEF
Msg after encryption: 238B4FE5 847E44B2
Msg after decryption: 01234567 89ABCDEF
Для Теста 2 используется более сложная схема шифрования. Два ключа a
и b
шифруются по очереди друг другом один миллион раз. В результате получается:
================ Test 2 ================
EEA9D0A2 49FD3BA6 B3436FB8 9D6DCA92
B2C95EB0 0C31AD71 80AC05B8 E83D696E
Этот результат соответствует данным для проверки CAST-128.
Файл cast128.cpp
:
#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
:
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 ]
. Далее над этими половинами производятся манипуляции. При этом в разных раундах используются разные функции преобразований:
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 ]
.
Скачать исходники реализации алгоритма шифрования CAST-128 на C++
Здравствуйте.
Такая ошибка происходит непосредственно при сборке или при запуске приложения? Если при запуске, то из среды разработки или из проводника?
tysik
Сборка под виндовс как подключить не дастающее библиотеки ?
QVector Не найдена в библиотеке Qt5Core.dll ?