IT Notes

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

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

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

Со словесным описанием работы алгоритма 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.

30ч939р с электроприводом.

Как для шифрования (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-блоков мы скоро дойдем.

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

До перехода к реализации напишем два небольших теста (см. 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.

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

Файл 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++

Похожие публикации

Комментарии

Сборка под виндовс как подключить не дастающее библиотеки ?

QVector Не найдена в библиотеке Qt5Core.dll ?

Здравствуйте.

Такая ошибка происходит непосредственно при сборке или при запуске приложения? Если при запуске, то из среды разработки или из проводника?