[C++] Advanced Encryption Standard (Rijndael)
От: gear nuke  
Дата: 07.05.07 18:07
Оценка: 22 (3)
Rijndael — симметричный блочный шифр с длиной ключа 128, 192 или 256 бит, принятый как стандарт у буржуев.

Данная реализация (согласно FIPS-197) не использует предвычесленных таблиц, поэтому компактна (+ не детектируется сигнатурными анализаторами криптоалгоритмов). За это пришлось поплатиться скоростью расширения ключа; тем не менее код (в отличае от большенства реализаций) оптимизирован под little-endian и скорость шифрования неплохая

MSVC с ключами /Ob2gity на Athlon x2 3600+
   key   | key expansions/sec | encryption, bytes/sec | decryption, bytes/sec
 128 bit |       1638400      |         76608292      |         71697504
 192 bit |       1458381      |         63072240      |         63310249
 256 bit |       1156092      |         53773128      |         53601329


Исходники дальше. В качестве примера использования — утилита командной строки, шифрующая файлы в режиме Cipher Block Chaining (CBC).
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
aes.hpp & aes.cpp
От: gear nuke  
Дата: 07.05.07 18:11
Оценка:
aes.hpp

/**\addtogroup AES ****************************************************////@{
/**\file*********************************************************************
 *                                                                     \brief
 *  Заголовочный файл класса #aes.
 *
 *  @author Serge Trusov
 *  mailto: serge.trusov@gmail.com
 *
 ****************************************************************************
 */ 


#ifndef _AES_HPP
#define _AES_HPP


/** 
 *  Базовый тип данных обрабатываемый криптоалгоритмом.
 */
typedef unsigned char   byte;


/////////////////////////////////////////////////////////////////////////////

/** 
 *  Класс обеспечивает шифрование блока данных
 *  по стандартам AES-128, AES-192 и AES-256.
 *
 *  @note
 *  При компиляции можно выбрать поддерживаемые варианты длины ключа
 *  задав любую комбинацию макросов #AES_128, #AES_192 и #AES_256.
 *  По умолчанию будут поддерживаться все возможные длины ключей.
 */
class   aes {

#if !defined(AES_128) && !defined(AES_192) && !defined(AES_256)
/// Обеспечивает поддержку ключей длиной 128 бит.       @ingroup AES
#define AES_128     
/// Обеспечивает поддержку ключей длиной 192 бит.       @ingroup AES
#define AES_192
/// Обеспечивает поддержку ключей длиной 256 бит.       @ingroup AES
#define AES_256
#endif

/////////////////////////////////////////////////////////////////////////////

public:

    /** 
     *  Значения, возвращаемые методами.
     *  Сигнализирует об успехе / ошибке.
     */
    enum   status   {
        Ok                  = 0,    ///< нет ошибки.
        wrong_key_lenght    = ~0    ///< неверная длина ключа.
    };

    /** 
     *  Размер блока шифротекста в байтах.
     *  По стандарту фиксирован.
     *  Может быть использован во внешних алгоритмах
     *  вызывающих методы класса.
     */
    static const unsigned  block_size = 16;

    /** 
     *  Шифрование блока данных.
     *  @note   Входной и выходной блоки могут совпадать !
     */
    void
      encrypt(
        const byte      in[block_size], ///< Входной блок данных.
        byte            out[block_size] ///< Выходной блок данных.  @see block_size
        );

    /** 
     *  Расшифровывание блока данных.
     *  @note   Входной и выходной блоки могут совпадать !
     */
    void
      decrypt(
        const byte      in[block_size], ///< Входной блок данных.
        byte            out[block_size] ///< Выходной блок данных. @see block_size
        );

    /** 
     *  Расширение ключа.
     *  Для (де)шифрации обязательно используется расширенный ключ.
     *  Необходимо вызвать метод перед первым encrypt() или decrypt()
     *  @note   Допустимые размеры ключа: 
     *  128 бит (если определён макрос #AES_128),
     *  192 бит (если определён макрос #AES_192),
     *  256 бит (если определён макрос #AES_265).
     */
    status
      expand_key(
        const byte    * key,            ///< Ключ.
        const unsigned  size            ///< Размер ключа в битах.
        );
   
/////////////////////////////////////////////////////////////////////////////

protected:

    /** 
     *  Количество раундов шифрования.
     *  Автоматически устанавливается в функции расширения ключа.
     */
    unsigned    rounds; 

    /** 
     *  Криптоалгоритм обрабатывает блоки по 4 байта.
     */
    typedef unsigned long   u32;

    /** 
     *  Расширенные (раундовые) ключи.
     *  Используются непосредственно для (де)шифрации текста.
     */
    u32     encryption_round_key[64],
            decryption_round_key[64];

    /** 
     *  Таблицы замен.
     */
    static  u32 s_box[256];     ///< Прямая замена
    static  u32 r_box[256];     ///< Обратная замена
    static  u32 ft[4][256];     ///< Расширенная прямая замена
    static  u32 rt[4][256];     ///< Расширенная обратная замена
    
    /** 
     *  Формирует таблицы замен.
     */
    void inline
      init_tables();
    
    /** 
     *  Образующий полином (базис) поля GF(2**8).
     *  определён так:     phi(x) = x**8 + x**4 + x**3 + x**1 + 1;
     */
    static const unsigned   basis = 1<<8 | 1<<4 | 1<<3 | 1<<1 | 1<<0;

    /** 
     *  Генератор Галуа (Linear Feedback Shift Register)
     *  ненулевых элементов поля GF(2**8) в заданном базисе.
     *  Выполняет умножение на x по модулю phi(x). (xTime)
     */
    byte static inline 
      lfsr2(
        byte    a           ///< Предыдущее состойние генератора.
        );

    /** 
     *  Циклический сдвиг двойного слова влево на байт.
     */
    u32 static inline 
      rot_left_8( 
        u32 value           ///< Сдвигаемое значение.
        );

    /** 
     *  Преобразуете 4 байта в двойное слово (32бита).
     */
    u32 static inline 
      get( 
        const byte array[4] ///< Указатель на массив из 4-х байт.
        );

    /** 
     *  Помещает двойное слово (32бита) в массив из четырёх байт. 
     */
    void static inline 
      put( 
        u32  value,         ///< Исходные данные.
        byte array[4]       ///< Указатель на массив из четырёх байт. 
        );

    /** 
     *  Вспомогательная функция прямой табличной замены.
     *  Один из байт двойного слова используется в качестве индекса. 
     */
    u32 static inline
      fb(
        u32 byte,           ///< Слово содержащее байт.
        unsigned position   ///< Позиция байта в слове.
        );

    /** 
     *  Вспомогательная функция обратной табличной замены.
     *  Один из байт двойного слова используется в качестве индекса. 
     */
    u32 static inline
      rb(
        u32 byte,           ///< Слово содержащее байт.
        unsigned position   ///< Позиция байта в слове.
        );

};//class   aes


#endif//#ifndef _AES_HPP


///========================================================================//
///*} EOF



aes.cpp

/**\addtogroup AES Advanced Encryption Standard******************************
 *
 *  Advanced Encryption Standard (Rijndael) algorithm implementation.
 *
 *  @author Serge Trusov
 *  mailto: serge.trusov@gmail.com
 *
 *  @version    0.5.05.21
 *  @date       21 May 2005
 *
 *  @note   Для тестирования работы скомпилируйте файл aes.cpp
 *          определив макрос AES_TEST   @see main()
 *
 *//**@{*/ /**  @file
 *                                                                     \brief
 *  Реализация класса #aes. 
 *  Обеспечивает (де)шифрацию блока данных по алгоритму Rijndael.
 *  Реализованы варианты: AES-128, AES-192 и AES-256.            @copydoc AES
 *
 ****************************************************************************
 */ 


#include <stdlib.h>
#include "aes.hpp"


//=========================================================================//
//
//  Перед использованием необходимо сгенерировать 
//  расширенный ключ (из 128-ми, 192-ч или 256-ти битного)
//
aes::status aes::expand_key( const byte * key, const unsigned size )
{
    unsigned i;

    //  инициализированный s_box содержит здесь ненулевое значение
    if( ! s_box[0] ) init_tables();

    //  Копируем ключ.

    u32   * ek = encryption_round_key;
    for( i = 0; i < size / (8*sizeof(u32)); i++ )
        ek[i] = get( &key[i * 4] );

    //  Создаём расширенный ключ для засшифровки

    u32 round_constant = 1;
    switch( size )
    {
    default: 
        return  wrong_key_lenght;

    #ifdef AES_128
    case 128:
        for( i = 0; i < 10; i++, ek += 4 )
        {
            ek[4] = ek[0]
                  ^ s_box[ ek[3] >>  0 & 0xFF ] << 24
                  ^ s_box[ ek[3] >> 24        ] << 16
                  ^ s_box[ ek[3] >> 16 & 0xFF ] <<  8
                  ^ s_box[ ek[3] >>  8 & 0xFF ] <<  0
                  ^ round_constant;
            ek[5] = ek[1] ^ ek[4];
            ek[6] = ek[2] ^ ek[5];
            ek[7] = ek[3] ^ ek[6];
            round_constant = lfsr2((byte)round_constant);
        }
        rounds = 10;
        break;
    #endif

    #ifdef AES_192
    case 192:
        for( i = 0; i < 8; i++, ek += 6 )
        {
            ek[6]  = ek[0]
                   ^ s_box[ ek[5] >>  0 & 0xFF ] << 24
                   ^ s_box[ ek[5] >> 24        ] << 16
                   ^ s_box[ ek[5] >> 16 & 0xFF ] <<  8
                   ^ s_box[ ek[5] >>  8 & 0xFF ] <<  0
                   ^ round_constant;
            ek[7]  = ek[1] ^ ek[6]; 
            ek[8]  = ek[2] ^ ek[7];
            ek[9]  = ek[3] ^ ek[8]; 
            ek[10] = ek[4] ^ ek[9];
            ek[11] = ek[5] ^ ek[10];
            //  упростим lfsr2, т.к кол-во итераций цикла < 9
            round_constant <<=1;// lfsr2( round_constant );
        }
        rounds = 12;
        break;
    #endif

    #ifdef AES_256
    case 256:
        for( i = 0; i < 7; i++, ek += 8 )
        {
            ek[8]  = ek[0]
                   ^ s_box[ ek[7] >>  0 & 0xFF ] << 24
                   ^ s_box[ ek[7] >> 24        ] << 16
                   ^ s_box[ ek[7] >> 16 & 0xFF ] <<  8
                   ^ s_box[ ek[7] >>  8 & 0xFF ] <<  0
                   ^ round_constant;
            ek[9]  = ek[1] ^ ek[8];
            ek[10] = ek[2] ^ ek[9];
            ek[11] = ek[3] ^ ek[10];
            ek[12] = ek[4]
                   ^ s_box[ ek[11] >> 24        ] << 24
                   ^ s_box[ ek[11] >> 16 & 0xFF ] << 16
                   ^ s_box[ ek[11] >>  8 & 0xFF ] <<  8
                   ^ s_box[ ek[11] >>  0 & 0xFF ] <<  0;
            ek[13] = ek[5] ^ ek[12];
            ek[14] = ek[6] ^ ek[13];
            ek[15] = ek[7] ^ ek[14];
            //  упростим lfsr2, т.к кол-во итераций цикла < 9
            round_constant <<=1;// lfsr2( round_constant );
        }
        rounds = 14;
        break;
    #endif
    }

    //  Создаём расширенный ключ для расшифровки

    u32   * dk = decryption_round_key;
    dk[0] = ek[0];  dk[1] = ek[1];  dk[2] = ek[2];  dk[3] = ek[3];

    for( i = 4 * (rounds-1); i; --i, dk++ )
    {
        //  Исходные данные расположены в таком порядке:
        //  ..., 9, 10, 11, 12, 5, 6, 7, 8, 1, 2, 3, 4.
        //  первый раз из 4х двигаем указатель "назад" на 7
        //  остальные три - "вперёд" на 1
        ek += i & 0x3 ? 1 : -7;
        dk[4] = rt[0][ s_box[ ek[4-1] >>  0 & 0xFF ] ]
              ^ rt[1][ s_box[ ek[4-1] >>  8 & 0xFF ] ]
              ^ rt[2][ s_box[ ek[4-1] >> 16 & 0xFF ] ]
              ^ rt[3][ s_box[ ek[4-1] >> 24        ] ];
    }
    dk[4] = ek[4-8];    dk[5] = ek[5-8];
    dk[6] = ek[6-8];    dk[7] = ek[7-8];

    return Ok;
}


//=========================================================================//
//
//  Шифрование
// 
void aes::encrypt( const byte in[block_size], byte out[block_size] )
{
    //  Указатель на раундовые константы
    u32   * rk = encryption_round_key;

    //  Считываем блок и добавляем раундовый ключ.
    u32     a0 = get( &in[ 0] ) ^ rk[0];
    u32     a1 = get( &in[ 4] ) ^ rk[1];
    u32     a2 = get( &in[ 8] ) ^ rk[2];
    u32     a3 = get( &in[12] ) ^ rk[3];
    u32     b0, b1, b2, b3;

    //  Раунды криптования (цикл развёрнут 2x)
    for( int i = rounds; ; rk += 8 )   
    {
        b0 = fb(a0, 0) ^ fb(a1, 1) ^ fb(a2, 2) ^ fb(a3, 3) ^ rk[4];
        b1 = fb(a1, 0) ^ fb(a2, 1) ^ fb(a3, 2) ^ fb(a0, 3) ^ rk[5];
        b2 = fb(a2, 0) ^ fb(a3, 1) ^ fb(a0, 2) ^ fb(a1, 3) ^ rk[6];
        b3 = fb(a3, 0) ^ fb(a0, 1) ^ fb(a1, 2) ^ fb(a2, 3) ^ rk[7];
        
        //  Сверхумный MSVC разворачивает цикл, что раздувает размер
        //  и снижает (!) скорость на 10%.
        //  приходится добавлять лишний байт :-(.
        #if defined(_MSC_VER) && ! defined(__cplusplus_cli)
            __asm nop
        #endif

        if( ! (i -= 2) ) break;

        a0 = fb(b0, 0) ^ fb(b1, 1) ^ fb(b2, 2) ^ fb(b3, 3) ^ rk[8];
        a1 = fb(b1, 0) ^ fb(b2, 1) ^ fb(b3, 2) ^ fb(b0, 3) ^ rk[9];
        a2 = fb(b2, 0) ^ fb(b3, 1) ^ fb(b0, 2) ^ fb(b1, 3) ^ rk[10];
        a3 = fb(b3, 0) ^ fb(b0, 1) ^ fb(b1, 2) ^ fb(b2, 3) ^ rk[11];
    }
      
    //  Последний раунд (без перемешивания столбцов)
    put(
        rk[8]  ^ s_box[ b3 >> 24        ] << 24
               ^ s_box[ b2 >> 16 & 0xFF ] << 16
               ^ s_box[ b1 >>  8 & 0xFF ] <<  8
               ^ s_box[ b0 >>  0 & 0xFF ] <<  0
        , &out[ 0]
        );
    put( 
        rk[9]  ^ s_box[ b0 >> 24        ] << 24
               ^ s_box[ b3 >> 16 & 0xFF ] << 16 
               ^ s_box[ b2 >>  8 & 0xFF ] <<  8 
               ^ s_box[ b1 >>  0 & 0xFF ] <<  0
        , &out[ 4]
        );   
    put( 
        rk[10] ^ s_box[ b1 >> 24        ] << 24
               ^ s_box[ b0 >> 16 & 0xFF ] << 16
               ^ s_box[ b3 >>  8 & 0xFF ] <<  8
               ^ s_box[ b2 >>  0 & 0xFF ] <<  0
        , &out[ 8]
        );  
    put( 
        rk[11] ^ s_box[ b2 >> 24        ] << 24
               ^ s_box[ b1 >> 16 & 0xFF ] << 16
               ^ s_box[ b0 >>  8 & 0xFF ] <<  8
               ^ s_box[ b3 >>  0 & 0xFF ] <<  0
        , &out[12]
        );
}


//=========================================================================//
//
//  Расшифровывание.
// 
void aes::decrypt( const byte in[block_size], byte out[block_size] )
{
    //  Указатель на раундовые константы
    u32   * rk = decryption_round_key;

    //  Считываем блок и добавляем раундовый ключ.
    u32     a0 = get( &in[ 0] ) ^ rk[0];
    u32     a1 = get( &in[ 4] ) ^ rk[1];
    u32     a2 = get( &in[ 8] ) ^ rk[2];
    u32     a3 = get( &in[12] ) ^ rk[3];
    u32     b0, b1, b2, b3;

    //  Раунды разкриптования (цикл развёрнут 2x)
    for( int i = rounds; ; rk += 8 )
    {
        b0 = rb(a0, 0) ^ rb(a3, 1) ^ rb(a2, 2) ^ rb(a1, 3) ^ rk[4];
        b1 = rb(a1, 0) ^ rb(a0, 1) ^ rb(a3, 2) ^ rb(a2, 3) ^ rk[5];
        b2 = rb(a2, 0) ^ rb(a1, 1) ^ rb(a0, 2) ^ rb(a3, 3) ^ rk[6];
        b3 = rb(a3, 0) ^ rb(a2, 1) ^ rb(a1, 2) ^ rb(a0, 3) ^ rk[7];

        //  Сверхумный MSVC разворачивает цикл, что раздувает размер
        //  и снижает (!) скорость.
        //  приходится добавлять лишний байт :-(.
        #if defined(_MSC_VER) && ! defined(__cplusplus_cli)
            __asm nop
        #endif

        if( ! (i -= 2) ) break;

        a0 = rb(b0, 0) ^ rb(b3, 1) ^ rb(b2, 2) ^ rb(b1, 3) ^ rk[8];
        a1 = rb(b1, 0) ^ rb(b0, 1) ^ rb(b3, 2) ^ rb(b2, 3) ^ rk[9];
        a2 = rb(b2, 0) ^ rb(b1, 1) ^ rb(b0, 2) ^ rb(b3, 3) ^ rk[10];
        a3 = rb(b3, 0) ^ rb(b2, 1) ^ rb(b1, 2) ^ rb(b0, 3) ^ rk[11];
    }
    
    //  Последний раунд (без перемешивания столбцов)
    put(
        rk[8]  ^ r_box[ b1 >> 24        ] << 24
               ^ r_box[ b2 >> 16 & 0xFF ] << 16
               ^ r_box[ b3 >>  8 & 0xFF ] <<  8
               ^ r_box[ b0 >>  0 & 0xFF ] <<  0
        , &out[ 0]
        );
    put(
        rk[9]  ^ r_box[ b2 >> 24        ] << 24
               ^ r_box[ b3 >> 16 & 0xFF ] << 16
               ^ r_box[ b0 >>  8 & 0xFF ] <<  8
               ^ r_box[ b1 >>  0 & 0xFF ] <<  0
        , &out[ 4]
        );
    put(
        rk[10] ^ r_box[ b3 >> 24        ] << 24
               ^ r_box[ b0 >> 16 & 0xFF ] << 16
               ^ r_box[ b1 >>  8 & 0xFF ] <<  8
               ^ r_box[ b2 >>  0 & 0xFF ] <<  0
        , &out[ 8]
        );
    put(
        rk[11] ^ r_box[ b0 >> 24        ] << 24
               ^ r_box[ b1 >> 16 & 0xFF ] << 16
               ^ r_box[ b2 >>  8 & 0xFF ] <<  8
               ^ r_box[ b3 >>  0 & 0xFF ] <<  0
        , &out[12]
        );
}

//@}    protected:


byte inline aes::lfsr2( byte a )
{
    return a & 0x80 ? a<<1 ^ (byte)basis    // вычитание по модулю 2
                    : a<<1;                 // нет обратноё связи
}


//=========================================================================//
/// !.
/// Создаёт прямую и обратную таблицы замен (S-box)
/// и расширенные таблицы замен/микширования -
/// используются для оптимизации криптораундов по скорости.
///
void aes::init_tables()
{
    //  Таблицы для быстрого умножения в поле GF(2**8)  
    byte    pow[1<<8],  log[1<<8];

    //  Заполняем их.
    unsigned    i = 0;      //  Начальная степень omega;
    byte        a = 1;      //  omega**i в полиномиальной форме;
    log[0] = 0;             //  Ой элемент нужен для генерации S-box
    do
    {
        pow[i] = a;         //  omega**i = x7**7 + .. + x0**0;
        log[a] = (byte)i;
        //  следующий элемент таблицы выбирается 
        //  на основе полинома phi(x+1)
        a ^= lfsr2(a);      //  a *= (x + 1)
    }
    while( (byte)++i );     //  2**8 итераций

    //  Что бы не инициализировать отдельно элементы [0x63, 0]
    //  используем "лишнюю" итерацию цикла;
    //   для этого модифицируем неиспользуемый элемент таблицы.
    pow[ (1<<8) - 1 ] = 0;

    //  Генерируем прямой и обратный S-box'ы
    i = 0;
    do
    {
        byte    a = pow[(1<<8)-1 - log[i]];

        //  Умножаем на     x**4 + x**3 + x**2 + x + 1
        a ^= a << 1 ^ a << 2 ^ a << 3 ^ a << 4  // a<<1|a>>7 == a<<1^a>>7 !
          ^  a >> 4 ^ a >> 5 ^ a >> 6 ^ a >> 7  // переставим слагаемые 
                                                // что бы помочь компилятору
        //  и прибавляем    x**6 + x**5 + x**1 + x**0
          ^                (1<<6 ^ 1<<5 ^ 1<<1 ^ 1<<0);   
        
        s_box[i] = a;
        r_box[a] = i;
    }
    while( (byte)++i );     //  2**8 итераций

    //  Генерируем расширенные таблицы замен
    for( i = 0; i < 256; i++ )
    {
        byte f = (byte)s_box[i]; 
        byte f2 = lfsr2(f);
        ft[0][i] = (f ^ f2) << 24 | f << 16 | f << 8 | f2;
        ft[1][i] = rot_left_8( ft[0][i] );
        ft[2][i] = rot_left_8( ft[1][i] );
        ft[3][i] = rot_left_8( ft[2][i] );

        byte r = (byte)r_box[i];
        //  Умножение выполняется по формуле:
        //  pow[(log[x] + log[y]) % 255]    ( при x и y != 0 )
        rt[0][i] = ! r ? r :
                 (u32) pow[(0x68 + log[r]) % 255] << 24 // log[0x0B] == 0x68
               ^ (u32) pow[(0xEE + log[r]) % 255] << 16 // log[0x0D] == 0xEE
               ^ (u32) pow[(0xC7 + log[r]) % 255] <<  8 // log[0x09] == 0xC7
               ^ (u32) pow[(0xDF + log[r]) % 255] <<  0;// log[0x0E] == 0xDF
        rt[1][i] = rot_left_8( rt[0][i] );
        rt[2][i] = rot_left_8( rt[1][i] );
        rt[3][i] = rot_left_8( rt[2][i] );
    }
}


aes::u32 inline aes::get( const byte array[4] )
{
#if (('1234' >> 24) == '1')     // little endian ?
    return  *(u32 *)(array);
#else
    return  array[0] <<  0
          | array[1] <<  8
          | array[2] << 16
          | array[3] << 24;
#endif
}


void inline aes::put( u32 value, byte array[4] )
{
#if (('1234' >> 24) == '1')     // little endian ?
    *(u32 *)(array) = value;
#else
    array[0] = value >>  0 & 0xFF;
    array[1] = value >>  8 & 0xFF;
    array[2] = value >> 16 & 0xFF;
    array[3] = value >> 24       ;
#endif
}


aes::u32 inline aes::fb(u32 byte, unsigned pos)
{
    return ft[pos][ byte >> pos*8 & 0xFF ];
}


aes::u32 inline aes::rb(u32 byte, unsigned pos)
{
    return rt[pos][ byte >> pos*8 & 0xFF ];
}


aes::u32    aes::s_box[256], aes::r_box[256];
aes::u32    aes::ft[4][256], aes::rt[4][256];


//=========================================================================//
// Блок используется при тестировании.
#ifdef AES_TEST
    #ifdef LIBST
        #include "libst.cpp"
        typedef long clock_t;
        clock_t clock(){ return GetTickCount(); }
        const clock_t CLOCKS_PER_SEC = 1000;
        extern "C" int _fltused = 0;
    #else
        #include <string.h>
        #include <stdio.h>
        #include <math.h>
        #include <time.h>
    #endif
#endif
//=========================================================================//


aes::u32 inline aes::rot_left_8( u32 value )
{
#ifdef  _M_IX86
    return  _rotl(value, 8);                // MSVC и Intel C++ делают ROL
#else
    return  value >> 24 | value << 8;
#endif
}


//=========================================================================//
// Блок используется при тестировании.
#ifdef AES_TEST
/** 
 *  Функция используется при компиляции тестового исполняемого файла.
 *  При тестировании проверяется корректность (де)шифрации
 *  и скорость различных методов класса aes при использовании
 *  ключей 128, 192 и 256 бит.
 */
int main()
{
    /**
     *  Данные для тестирования взяты из fips-197.pdf
     *  http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
     */
    
    static const byte plaintext[aes::block_size] = 
    {   
        0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
        0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
    };

    // Для "коротких" ключей используется начальная часть всего ключа.
    static const byte key[] = 
    {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        // 192 бит
        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
        // 256 бит
        0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
    };

    static const byte ciphertext[3][aes::block_size] =
    {
        //  для ключа 128 бит
        {0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, 
         0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a},
        //  для ключа 192 бит
        {0xdd, 0xa9, 0x7c, 0xa4, 0x86, 0x4c, 0xdf, 0xe0,
         0x6e, 0xaf, 0x70, 0xa0, 0xec, 0x0d, 0x71, 0x91},
        //  для ключа 256 бит
        {0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf, 
         0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89}
    };

    aes     crypto;
    byte    buf[crypto.block_size];

    printf("\n AES test:\n");

    for( int i0  = 0; i0 <= 2; i0++ )
    {
        int key_length = 128 + 64 * i0;
        printf("\n %d bit key... ", key_length);

        if( crypto.expand_key(key, key_length) != crypto.Ok )
            printf("error: can't expand key.");
        else
        {
            crypto.encrypt(plaintext, buf);
            if( memcmp(&ciphertext[i0], buf, crypto.block_size) )
                printf("error: encryption failed.");
            else
            {
                crypto.decrypt(buf, buf);
                if( memcmp(plaintext, buf, crypto.block_size) )
                    printf("error: decryption failed.");
                else
                    printf("Ok.");
            }
        }
    }
        
    printf("\n\n Speed test:");
    printf("\n\n   key   | key expansions/sec | encryption, bytes/sec | decryption, bytes/sec");
    for( int i1  = 0; i1 <= 2; i1++ )
    {
        const int times = 1024 * 1024;
        int key_length = 128 + 64 * i1;
        printf("\n %3d bit |", key_length);

        clock_t start = clock();
        for( int t0 = times; t0; t0-- )
        {
            crypto.expand_key(key, key_length);
        }
        printf("    %10d      |",
           (unsigned)((double)times  * CLOCKS_PER_SEC / (clock() - start))
           );
        
        crypto.encrypt(plaintext, buf);

        start = clock();
        for( int t1 = times; t1; t1-- )
        {
            crypto.encrypt(buf, buf);
        }
        printf("       %10d      |", (unsigned)
           ((double)crypto.block_size * times * CLOCKS_PER_SEC / (clock() - start))
           );

        start = clock();
        for( int t2 = times; t2; t2-- )
        {
            crypto.decrypt(buf, buf);
        }
        printf("       %10d", (unsigned)
           ((double)crypto.block_size * times * CLOCKS_PER_SEC / (clock() - start))
           );

        crypto.decrypt(buf, buf);
        if( memcmp(plaintext, buf, crypto.block_size) )
        {
            printf("\n\n error: Monte Carlo test failed.\n");
            return ~0;
        }
    }
    printf("\n\n Ok.\n");    
    return 0;
}
#endif//#ifdef AES_TEST
//=========================================================================//

///========================================================================//
/// EOF
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
шифратор файлов в режиме CBC
От: gear nuke  
Дата: 07.05.07 18:16
Оценка:
/**\file*********************************************************************
 *                                                                     \brief
 *  AES CBC mode implementation
 *
 *  Обеспечивает (де)шифрацию файлов по алгоритму AES
 *  (возможные ключи: 128, 192 и 256 бит)
 *  в режиме Cipher Block Chaining (CBC).
 *
 *  @author Serge Trusov
 *  mailto: serge.trusov@gmail.com
 *
 *  @date   02 April 2005
 *
 *  @note   Возможные параметры командной строки:
 *          /e      - режим шифрования;
 *          /d      - режим расшифровывания;
 *          2 и более имен файлов;
 *          ключ выбирается автоматически из первого бинарного файла
 *          длиной 16, 24 или 32 байт.
 *          Длина зашифрованного файла округляется вверх до 16 байт.
 *
 *  @warning    Как следует не тестировалось, но вроде бы работает :-)
 *
 ****************************************************************************
 */ 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "aes.cpp"


int main( int arg_count, char * argument[])
{
    static char
    help[] =    "\nusage: aest option keyfile filename"
                "\noptions:" 
                "\n/e   encrypt"
                "\n/d   decrypt\n"
                "\n warning! filesize will be rounded up by 16 bytes.";

    printf("\nAES (CBC mode) crypto tool v0.1\n");

    if( arg_count <= 2 )
    {
            printf(help);
            return ~0;
    }
    
    aes         crypto;     int i;

    const int   max_key = 256/8;
    byte        key[max_key+1]; // Резервируем лишний байт для чтения.
                                // Нужно для определения длины ключевого файла.

    void (aes:: * whats_to_do)(
                    const byte  [aes::block_size], 
                    byte        [aes::block_size]
                    ) = 0;

    // Исходя из опций командной строки, определяем, что делать.
    // Допустима только ОДНА опция.
    // Иначе - ошибка! Выводим help.
    for( i = 1; i < arg_count; i++ )
    {
        if( * argument[i] != '-' && * argument[i] != '/' )
            continue;

        switch( *(argument[i]+1) | 0x20 )   // любой регистр.
        {
        case 'd':
            if( whats_to_do )
            {
                printf(help);
                return ~0;
            }
            printf("\ndecrypt mode");
            whats_to_do = &aes::decrypt;
            break;
         case 'e':
            if( whats_to_do )
            {
                printf(help);
                return ~0;
            }
            printf("\nencrypt mode");
            whats_to_do = &aes::encrypt;
            break;
        }
         
        // "Убираем" опцию из списка аргументов командной строки
        // что бы не обрабатывать её как имя файла позже.
        if( whats_to_do )
        {
            // Имя ключевого файла может быть сразу за опцией
            // без пробелов!
            // В таком случае, сдвигаем указатель.
            if( *(argument[i]+2) > ' ')
                argument[i] += 2;
            else
                argument[i]  = 0;
        }
    }

    // Если опция не указана, выходим.
    if( ! whats_to_do )
    {
        printf(help);
        return ~0;
    }

    // Простматриваем файлы указанные в командной строке.
    // Превый файл длиной 128, 192 или 256 байт 
    // содержит ключ (де)шифровки.
    for( i = 1; i < arg_count; i++ )
    {
        if( ! argument[i] ) continue;

        FILE * f = fopen( argument[i], "rb" );
        if( ! f )
        {
            printf("\nerror: can't read file `%s'", argument[i]);
            return ~0;
        }

        // Попытаемся считатьт больше, чем максимальная длина ключа.
        int key_size = 8 * fread( key, sizeof(byte), max_key+1, f );
        fclose( f );

        switch( key_size )
        {
        case 128:
        case 192:
        case 256:
            printf("\nusing %d bit keyfile `%s'...", key_size, argument[i]);
            crypto.expand_key( key, key_size );
            argument[i] = 0;    // Файл обработан.
            goto key_Ok;
        }
    }
    printf("\nno keyfile found.");
    return ~0;

key_Ok:
    for( i = 1; i < arg_count; i++ )
    {
        if( ! argument[i] ) continue;

        printf("\nprocessing file `%s'...", argument[i]);
        FILE * in  = fopen( argument[i], "rb" );
        FILE * out = fopen( argument[i], "r+b" );
        if( ! in || ! out )
        {
            printf(" can't open.");
            continue;
        }

        byte    buf[crypto.block_size];
        byte    feedback[crypto.block_size] = {0};

        while( unsigned chunk = fread(buf, sizeof(byte), crypto.block_size, in) )
        {
            // CBC mode.
            // Обратная cвязь по шифротексту.
            
            if( whats_to_do == &aes::encrypt)
            {
                // Если считано меньше, чем минимально допустимый блок,
                // дополняем буфер нулями.
                for( int pos = 0; pos < crypto.block_size; pos++ )
                    buf[pos] = pos < chunk ? buf[pos] ^ feedback[pos] : 0;
                (crypto.*whats_to_do)(buf, buf);
                memcpy(feedback, buf, crypto.block_size);
            }

            if( whats_to_do == &aes::decrypt)
            {
                byte    plain[crypto.block_size];
                (crypto.*whats_to_do)(buf, plain);
                for( int pos = 0; pos < crypto.block_size; pos++ )
                {
                    byte tmp = buf[pos];
                    buf[pos] = plain[pos] ^ feedback[pos];
                    feedback[pos] = tmp;
                }
            }

            if( fwrite(buf, sizeof(byte), crypto.block_size, out) 
                < crypto.block_size )
            {
                printf("error: can't write to file `%s'.", argument[i]);
                break;
            }
        }
        if( feof (in) ) printf(" Ok.");

        fclose( in );
        fclose( out );
    }
    return 0;
}


//=========================================================================//
// EOF
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re: aes.hpp & aes.cpp
От: AgentRX Россия  
Дата: 25.09.07 13:24
Оценка:
Здравствуйте, gear nuke, Вы писали:


static const unsigned   basis = 1<<8 | 1<<4 | 1<<3 | 1<<1 | 1<<0;
byte inline aes::lfsr2( byte a )
{
    return a & 0x80 ? a<<1 ^ (byte)basis    // вычитание по модулю 2
                    : a<<1;                 // нет обратноё связи
}


Возникает вопрос: зачем постоянно урезать полином?
Уважайте собеседника, даже если не согласны с его мнением
Re[2]: aes.hpp & aes.cpp
От: AgentRX Россия  
Дата: 25.09.07 14:46
Оценка:
Вот еще:

void aes::init_tables()
{
byte        a = 1;      //  omega**i в полиномиальной форме;
...
do
 {
  byte    a = pow[(1<<8)-1 - log[i]];


Это обе омеги или просто случайное локальное переопределение переменной?
Уважайте собеседника, даже если не согласны с его мнением
Re[2]: aes.hpp & aes.cpp
От: AgentRX Россия  
Дата: 25.09.07 14:51
Оценка:
Вот еще:

void aes::init_tables()
{
byte        a = 1;      //  omega**i в полиномиальной форме;
...
do
 {
  byte    a = pow[(1<<8)-1 - log[i]];


Это обе омеги или просто случайное локальное переопределение переменной?
Уважайте собеседника, даже если не согласны с его мнением
Re[2]: aes.hpp & aes.cpp
От: gear nuke  
Дата: 28.09.07 08:59
Оценка:
Здравствуйте, AgentRX, Вы писали:

ARX>зачем постоянно урезать полином?


В расчетах 8й бит не используется, но полином определён с ним что бы было больше похоже на доки.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[3]: aes.hpp & aes.cpp
От: gear nuke  
Дата: 28.09.07 08:59
Оценка:
Здравствуйте, AgentRX, Вы писали:

ARX>Это обе омеги или просто случайное локальное переопределение переменной?


Это разные переменные, надеялся, что будет видно из комментариев.
    //  Таблицы для быстрого умножения в поле GF(2**8)  
    byte    pow[1<<8],  log[1<<8];

    //  Заполняем их.
    unsigned    i = 0;      //  Начальная степень omega;
    byte        a = 1;      //  omega**i в полиномиальной форме;
    log[0] = 0;             //  Ой элемент нужен для генерации S-box
    do
    {
        pow[i] = a;         //  omega**i = x7**7 + .. + x0**0;
        log[a] = (byte)i;
        //  следующий элемент таблицы выбирается 
        //  на основе полинома phi(x+1)
        a ^= lfsr2(a);      //  a *= (x + 1)
    }
    while( (byte)++i );     //  2**8 итераций

    //  Что бы не инициализировать отдельно элементы [0x63, 0]
    //  используем "лишнюю" итерацию цикла;
    //   для этого модифицируем неиспользуемый элемент таблицы.
    pow[ (1<<8) - 1 ] = 0;

    //  Генерируем прямой и обратный S-box'ы
    i = 0;
    do
    {
        byte    a = pow[(1<<8)-1 - log[i]];

Почему такие имена уже не вспомню, но вообще да, неудачно смотрится рядом со счётчиками циклов совместимыми со старым С++.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.