Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > OpenSSL: баг реализации Blowfish


Автор: Alca 17.12.2008, 18:53
OpenSSL: баг реализации Blowfish.
Скачал исходники отсюда: http://www.openssl.org/source/openssl-0.9.8i.tar.gz
Заметил, что если шифрую ключем, например:
Код

1111 или 111111 (т.е. все символы одинаковые),

то можно данные расшифровать ключом: 
Код

1
или
11
или
111
и т.д.

Единицы можно заменить на любой символ и всё будет расшифровываться.
Может кто-то сталкивался с этой проблемой?

Автор: jonie 18.12.2008, 08:38
значит не так шифруешь.

Автор: Alca 18.12.2008, 09:51
Не спорю.

Автор: Alca 18.12.2008, 15:43
Выкладываю исходники. Если есть критика - буду рад. smile 
Код

#include "openssl-0.9.8i\blowfish.h"
//...    
//---------------------------------------------------------------------------
//TODO: bBlowfishCrypt 
bool bBlowfishCrypt(FILE *pFileIn, FILE *pFileOut, int iMode, const string &сsBlowfishCryptKey) { 
    //-------------------------------------
    //CHECK
    if (NULL == pFileIn || NULL == pFileOut) {
        return false;
    }
    if (!(BF_ENCRYPT == iMode) && !(BF_DECRYPT == iMode) ) {
        return false;
    }
    if (true == сsBlowfishCryptKey.empty()) {
        return false;
    }

    //-------------------------------------
    //переменные
    int                iNum        = 0;
    const unsigned int cuiBuffSize = 1024;
    unsigned char      ucBuffIn [cuiBuffSize];    memset(ucBuffIn,  0, sizeof(unsigned char) * cuiBuffSize);
    unsigned char      ucBuffOut[cuiBuffSize];    memset(ucBuffOut, 0, sizeof(unsigned char) * cuiBuffSize);
      
    //-------------------------------------
    //Ключ шифрования длиной 128 бит и вектор инициализации 
    unsigned char ucIv[8] = {'0', '1', '2', '3', '4', '5', '6', '7'};    //memset(ucIv, 0, sizeof(unsigned char) * 8);

    size_t uiKeySize = sizeof(unsigned char) * сsBlowfishCryptKey.size();
    unsigned char *pucKey = (unsigned char *)malloc(uiKeySize);
    if (pucKey == NULL) {
        return false;
    }
    memset(pucKey, 0, uiKeySize);

    if (false == bStrToUCHAR(сsBlowfishCryptKey, pucKey, сsBlowfishCryptKey.size())) {
        /*LOG*/Console.bWriteErrLine("bStrToUCHAR() == false");
        free(pucKey);    
        pucKey = NULL;

        return false;
    }
      
    //-------------------------------------
    //Помещаем ключ в структуру bfkey
    BF_KEY bfkey;
    BF_set_key(&bfkey, uiKeySize, pucKey);    
    
    free(pucKey);    
    pucKey = NULL;
     
    //-------------------------------------
    //Определяем что за файл (шифр / нешифр)
    switch (iMode) {
        case BF_ENCRYPT:    
            {
             //запись штампа в вых. файл
                size_t uiWrited = fwrite(g_ucBlowfishStamp, 1, g_culBlowfishStampSize, pFileOut);
                if (uiWrited != g_culBlowfishStampSize) {
                    /*LOG*/Console.bWriteErrLine("uiWrited != g_culBlowfishStampSize");
                    return false;
                }
                break;
            }
        
        case BF_DECRYPT:    
            {
                //читаем файл начиная с конца штампа
                if (0 != fseek(pFileIn, g_culBlowfishStampSize, SEEK_SET)) {
                    return false;
                }    
                break;    
            }    
    }
    
    //-------------------------------------
    //Шифруем / расшифровуем блоки входного файла
    for (;;) { 
        size_t uiInLen = fread(ucBuffIn, 1, cuiBuffSize, pFileIn); 
        if (uiInLen <= 0) {
            break; 
        }
          
        BF_cfb64_encrypt(ucBuffIn, ucBuffOut, uiInLen, &bfkey, ucIv, &iNum , iMode); 
        
        size_t uiWrited = fwrite(ucBuffOut, 1, uiInLen, pFileOut); 
        if (uiWrited != uiInLen) {
            /*LOG*/Console.bWriteErrLine("uiWrited != uiInLen");
            return false;
        }
    } 

    //-------------------------------------
    //файл шифрован, дописать в конец вых. файла шифрованый ucVerifyStamp
    if (BF_ENCRYPT == iMode) {
        unsigned char ucBuffOut2[g_culVerifyStampSize];        memset(ucBuffOut2, 0, sizeof(ucBuffOut2));
        
        BF_cfb64_encrypt(g_ucVerifyStamp, ucBuffOut2, g_culVerifyStampSize, &bfkey, ucIv, &iNum , iMode); 
        
        size_t uiWrited = fwrite(ucBuffOut2, 1, g_culVerifyStampSize, pFileOut);
        if (uiWrited != g_culVerifyStampSize) {
            /*LOG*/Console.bWriteErrLine("uiWrited != g_culVerifyStampSize");
            return false;
        }
    }
    
    //-------------------------------------
    //файл дешифрован, проверяем что в конце есть ucVerifyStamp
    if (BF_DECRYPT == iMode) {
        //прочитать последние culVerifyStampSize байты
        unsigned char     ucBuffOut3[g_culVerifyStampSize];        memset(ucBuffOut3, 0, sizeof(ucBuffOut3));
        unsigned long int ulFileOutSize = ulFileSize(pFileOut);
        
        if (0 != fseek(pFileOut, ulFileOutSize - g_culVerifyStampSize, SEEK_SET)) {
            return false;
        }
        size_t uiOutLen = fread(ucBuffOut3, 1, g_culVerifyStampSize, pFileOut);
        if (g_culVerifyStampSize != uiOutLen) {
            return false;
        }
        rewind(pFileOut);

        //сравниваем со ucVerifyStamp (0 - equal)
        int iRes = memcmp((const void *)g_ucVerifyStamp, (const void *)ucBuffOut3, g_culVerifyStampSize);
        if (0 != iRes) {
            return false;
        }

        //есть ucVerifyStamp -> удаляем с конца "VerifySignature"
        if (false == bCutFileFromEnd(pFileOut, g_culVerifyStampSize)) {
            return false;
        }            
    }    

    return true; 
}
//...
int main() {
    //...
    //-------------------------------------
    //создаём вх, вых файлы 
    FILE *pFileIn = fopen(sFilePathIn.c_str(), "rb+");
    if (NULL == pFileIn) {
        /*INFO*/Console.bWriteErrLine("Не могу открыть входной файл");
        return false;
    }
    sFilePathOut = sFilePathIn + ".{" + sCreatePlainGUID() + "}";    //добавить расширение ".tmp"
    FILE *pFileOut = fopen(sFilePathOut.c_str(), "wb+");
    if (NULL == pFileOut) {
        /*INFO*/Console.bWriteErrLine("Не могу открыть выходной файл");
        return false;
    }

    //-------------------------------------
    //шифр / дешифр
    bool bIsCrypted = bBlowfishCrypt(pFileIn, pFileOut, iBlowfishCryptMode, сsBlowfishCryptKey); 

    //-------------------------------------
    //закрываем вх, вых файлы
    fflush(pFileOut);    fclose(pFileOut);
    fflush(pFileIn);    fclose(pFileIn);
    //...
}


Автор: Alca 26.12.2008, 22:48
Нужна помощь. smile 

Автор: Alca 26.12.2008, 23:28
Цитата

значит не так шифруешь.

Вот инициализирую ключ:
Код

BF_KEY bfkey;        memset(&bfkey, 0, sizeof(bfkey));
BF_set_key(&bfkey, uiKeySize, pucKey);
    
А вот его значение после, при таких парольных фразах:
Код

pucKey = "1";
uiKeySize = 1;

и
Код

pucKey = "1111";
uiKeySize = 4;

Результат одинаковый.

Автор: xvr 27.12.2008, 13:01
А что такое bStrToUCHAR?

Автор: Alca 27.12.2008, 17:53
Код

bool bStrToUCHAR(const std::string &csStr, unsigned char *ucBuff, unsigned int uiBuffLen) {
    for (unsigned int i = 0; i < csStr.size(); i ++)  { 
        ucBuff[i]  = static_cast<unsigned char>(csStr.at(i)); 
    }

    return true;
}

Автор: xvr 27.12.2008, 19:29
Цитата(Alca @ 26.12.2008,  23:28)
Цитата

значит не так шифруешь.

Вот инициализирую ключ:
Код

BF_KEY bfkey;        memset(&bfkey, 0, sizeof(bfkey));
BF_set_key(&bfkey, uiKeySize, pucKey);
    
А вот его значение после, при таких парольных фразах:
Код

pucKey = "1";
uiKeySize = 1;

и
Код

pucKey = "1111";
uiKeySize = 4;

Результат одинаковый.

Все очень просто, смотрим реализацию BF_set_key:
Код

void BF_set_key(BF_KEY *key, int len, const unsigned char *data)
    {
    int i;
    BF_LONG *p,ri,in[2];
    const unsigned char *d,*end;


    memcpy(key,&bf_init,sizeof(BF_KEY));
    p=key->P;

    if (len > ((BF_ROUNDS+2)*4)) len=(BF_ROUNDS+2)*4;

    d=data;
    end= &(data[len]);
    for (i=0; i<(BF_ROUNDS+2); i++)
        {
        ri= *(d++);
        if (d >= end) d=data;

        ri<<=8;
        ri|= *(d++);
        if (d >= end) d=data;

        ri<<=8;
        ri|= *(d++);
        if (d >= end) d=data;

        ri<<=8;
        ri|= *(d++);
        if (d >= end) d=data;

        p[i]^=ri;
        }
Т.е. исходный пароль раскладывается в поля ключа, длинна этого пароля - (BF_ROUNDS+2)*4 (что равно 72). Если пароль короче, то он повторяется сначала. Так как у тебя пароль состоит из разного числа '1', то в результате получается эквивалентный пароль из 72х единиц, сколько бы изначально ты единиц не подавал  smile Добавь в качестве первого символа в пароль длинну строки с паролем (можно прямо в бинарном виде)

Автор: shara 28.12.2008, 00:32
как все просто оказалось

xvr smile 

Автор: Alca 29.12.2008, 14:35
Цитата

Добавь в качестве первого символа в пароль длинну строки с паролем (можно прямо в бинарном виде)

Эт как? Пароль не должен состоять из одинаковых символов?

Автор: xvr 29.12.2008, 15:03
Цитата(Alca @ 29.12.2008,  14:35)
Цитата

Добавь в качестве первого символа в пароль длинну строки с паролем (можно прямо в бинарном виде)

Эт как? Пароль не должен состоять из одинаковых символов?

Желательно. И уж точно не должен состоять из N единичек - такой пароль вскроют в секунды.

Автор: Alca 29.12.2008, 15:08
Его еще быстее подберут, если вместо пароля "777777" прокатит "7".
Юзер то наверное не вкурсе про такой прикол. 
Я считаю, что это какая-то глючная реализация BlowFish. 
Чем думали те кто этот код писал? Или это так должно быть по алгоритму? 

Автор: xvr 29.12.2008, 22:58
Цитата(Alca @ 29.12.2008,  15:08)
Его еще быстее подберут, если вместо пароля "777777" прокатит "7".
Юзер то наверное не вкурсе про такой прикол. 
Я считаю, что это какая-то глючная реализация BlowFish. 
Чем думали те кто этот код писал? Или это так долно быть по алгоритму?

Похоже функция BF_set_key не расчитанна на прямую генерацию BF ключа из пароля, она ожидает бинарный ключ.
Цитата из openssl-0.9.8i/doc/ssleay.txt:
Цитата

To generate a password from a text string, I suggest using MD5 (or MD2) to
produce a 16 byte message digest that can then be passed directly to
BF_set_key().

Автор: xvr 29.12.2008, 23:14
Посмотрел еще раз исходники - ключ генерируется из пароля в функции EVP_BytesToKey (файл openssl-0.9.8i/crypto/evp/evp_key.c). Действительно используется дайжест и еще что-то, не разбирался - функция не очень тривиальная  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)