Дополнительный блок Crypto ++ в конце зашифрованного потока в режиме AES-128 CBC - PullRequest
0 голосов
/ 20 февраля 2019

Я пытаюсь зашифровать 320 байтов двоичных данных с помощью AES-128 в режиме CBC и сохранить шифр в файл.Выходной файл должен был иметь 320 байтов, но я получил 336 байтов.Вот мой код:

#include <iostream>
#include <fstream>
#include <crypto++/aes.h>
#include <crypto++/modes.h>
#include <crypto++/base64.h>
#include <crypto++/sha.h>
#include <cryptopp/osrng.h>
#include <crypto++/filters.h>
#include <crypto++/files.h>

namespace CryptoPP
{
    using byte = unsigned char;
}

void myAESTest()
{
    std::string password = "testPassWord";

    // hash the password string
    // -------------------------------
    CryptoPP::byte key[CryptoPP::AES::DEFAULT_KEYLENGTH], iv[CryptoPP::AES::BLOCKSIZE];
    CryptoPP::byte passHash[CryptoPP::SHA256::DIGESTSIZE];
    CryptoPP::SHA256().CalculateDigest(passHash, (CryptoPP::byte*) password.data(), password.size());
    std::memcpy(key, passHash, CryptoPP::AES::DEFAULT_KEYLENGTH);
    std::memcpy(iv, passHash+CryptoPP::AES::DEFAULT_KEYLENGTH, CryptoPP::AES::BLOCKSIZE);

    // encrypt
    // ---------------------------------
    int chunkSize = 20*CryptoPP::AES::BLOCKSIZE;

    CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encryptor;
    encryptor.SetKeyWithIV(key, sizeof(key), iv);

    std::ofstream testOut("./test.enc", std::ios::binary);
    CryptoPP::FileSink outSink(testOut);

    CryptoPP::byte message[chunkSize];

    CryptoPP::StreamTransformationFilter stfenc(encryptor, new CryptoPP::Redirector(outSink));

    for(int i = 0; i < chunkSize; i ++)
    {
        message[i] = (CryptoPP::byte)i;
    }

    stfenc.Put(message, chunkSize);
    stfenc.MessageEnd();
    testOut.close();

    // decrypt
    // ------------------------------------
    // Because of some unknown reason increase chuksize by 1 block
    // chunkSize+=16;

    CryptoPP::byte cipher[chunkSize], decrypted[chunkSize];

    CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryptor;
    decryptor.SetKeyWithIV(key, sizeof(key), iv);

    std::ifstream inFile("./test.enc", std::ios::binary);
    inFile.read((char *)cipher, chunkSize);

    CryptoPP::ArraySink decSink(decrypted, chunkSize);
    CryptoPP::StreamTransformationFilter stfdec(decryptor, new CryptoPP::Redirector(decSink));

    stfdec.Put(cipher, chunkSize);
    stfdec.MessageEnd();
    inFile.close();

    for(int i = 0; i < chunkSize; i++)
    {
        std::cout << (int)decrypted[i] << ' ';
    }
    std::cout << std::endl;
}

int main(int argc, char* argv[]) 
{
    myAESTest();
    return 0;
}

Я не могу понять, как генерируются последние 16 байтов.Если я решу игнорировать последние 16 байтов в расшифровке, CryptoPP выдает CryptoPP::InvalidCiphertext error:

terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
  what():  StreamTransformationFilter: invalid PKCS #7 block padding found

1 Ответ

0 голосов
/ 20 февраля 2019

Я не могу понять, как генерируются последние 16 байтов.Если я решу игнорировать последние 16 байтов в расшифровке, Crypto ++ выбрасывает InvalidCiphertext error

Последние 16 байтов заполнены.Заполнение добавляется фильтром StreamTransformationFilter;см. Ссылка на класс StreamTransformationFilter в руководстве.Хотя не очевидно, DEFAULT_PADDING - это PKCS_PADDING для ECB_Mode и CBC_Mode.Это NO_PADDING для других режимов, таких как OFB_Mode и CTR_Mode.

Необходимо указать NO_PADDING только для фильтров шифрования и дешифрования.Однако вы должны убедиться, что открытый текст и зашифрованный текст кратны размеру блока, что равно 16 для AES.

Вы можете обойти ограничение размера блока, переключившись на другой режим, например CTR_Mode.Но тогда вы должны быть очень осторожны с повторным использованием ключа или IV, что может быть затруднительно из-за используемой вами схемы получения пароля.

Вместо:

CBC_Mode<AES>::Encryption encryptor;
...
StreamTransformationFilter stfenc(encryptor, new Redirector(outSink));

Использование:

CBC_Mode<AES>::Encryption encryptor;
...
StreamTransformationFilter stfenc(encryptor, new Redirector(outSink), NO_PADDING);

Также см. CBC_Mode в вики Crypto ++.Вас также может заинтересовать Authenticated Encryption в вики.


Для этого вы также можете:

#ifndef CRYPTOPP_NO_GLOBAL_BYTE
namespace CryptoPP
{
    using byte = unsigned char;
}
#endif

CRYPTOPP_NO_GLOBAL_BYTE определяется после C ++ 17 std::byte Исправления .Если CRYPTOPP_NO_GLOBAL_BYTE не определено, то byte находится в глобальном пространстве имен (Crypto ++ 5.6.5 и более ранние).Если определено CRYPTOPP_NO_GLOBAL_BYTE, то byte находится в пространстве имен CryptoPP (Crypto ++ 6.0 и более поздних версий).


Для этого:

std::ofstream testOut("./test.enc", std::ios::binary);
FileSink outSink(testOut);

Вы также можете сделать:

FileSink outSink("./test.enc");

Для этого:

SHA256().CalculateDigest(passHash, (byte*) password.data(), password.size());
std::memcpy(key, passHash, AES::DEFAULT_KEYLENGTH);
std::memcpy(iv, passHash+AES::DEFAULT_KEYLENGTH, AES::BLOCKSIZE);

Вы можете рассмотреть возможность использования HKDF в качестве функции деривации.Используйте один пароль, но две разные метки, чтобы обеспечить независимое получение.Одной меткой может быть строка "AES key derivation version 1", а другой меткой может быть "AES iv derivation version 1".

Метка будет использоваться в качестве параметра info для DeriveKey.Вам просто нужно вызвать его дважды, один раз для ключа и один раз для iv.

unsigned int DeriveKey (byte *derived, size_t derivedLen,
    const byte *secret, size_t secretLen,
    const byte *salt, size_t saltLen,
    const byte *info, size_t infoLen) const

secret - пароль.Если у вас есть salt, используйте его.В противном случае HKDF использует соль по умолчанию.

Также см. HKDF на вики-сайте Crypto ++.


Наконец, относительно этого:

Вы можете обойти ограничение размера блока, переключившись на другой режим, например CTR_Mode.Но тогда вы должны быть очень осторожны с повторным использованием ключа или IV, что может быть затруднительно из-за используемой вами схемы получения пароля.

Вы также можете рассмотреть Интегрированную схему шифрования, например Схема интегрированного шифрования эллиптической кривой .Это IND-CCA2 , что является сильным понятием безопасности.Все, что вам нужно, упаковано в схему шифрования.

В соответствии с ECIES каждый пользователь получает пару открытого / закрытого ключей.Затем большой случайный секрет используется в качестве начального числа для ключа AES, ключа iv и mac.Открытый текст зашифрован и аутентифицирован.Наконец, семя зашифровано под открытым ключом пользователя.Пароль все еще используется, но он используется для расшифровки закрытого ключа.

...