简体   繁体   中英

AES/CTR decryption stops unexpectedly using Crypto++?

I am trying to AES encrypt and decrypt a byte array ( vector<unsigned char> ) with the Crypto++ library . Encryption works correctly.

My function generates a random IV and puts it in front of the encrypted data. Decryption of course reads the IV. I modified the code from this answer, so it works with my vectors, and CTR/No Padding.

The program crashes in the line stfDecryptor.Put(...) . First I thought the data or IV is read not correctly, but after checking, this is not the case.

I hope I'm making a really obvious mistake here.

Thanks guys :)

Class call:

//The code is not clean, i want to get it working first and then clean it up. 
auto key = Helper::RandomBytes(16); //returns "byte Array" with random Bytes
auto data = Helper::UTF8String2ByteArray("HELLO WORLD!");
auto encrypted = AESCrypt::encrypt(key, data);
cout << Helper::ByteArray2HexString(key);
cout << "\r\n";
cout << Helper::ByteArray2HexString(data);
cout << "\r\n";
cout << Helper::ByteArray2HexString(encrypted);
cout << "\r\n";    
auto decrypted = AESCrypt::decrypt(key, encrypted);
auto clear = Helper::ByteArray2UTF8String(decrypted);

Class:

vector<unsigned char> AESCrypt::encrypt(vector<unsigned char> key_, vector<unsigned char> data)
{
    vector<unsigned char> iv_ = Helper::RandomBytes(16); //returns "byte Array" with random Bytes

    byte key[AES::DEFAULT_KEYLENGTH], iv[AES::BLOCKSIZE];

    for (size_t i = 0; i < AES::DEFAULT_KEYLENGTH; i++)
        key[i] = key_[i];
    for (size_t i = 0; i < iv_.size(); i++)
        iv[i] = iv_[i];

    AES::Encryption aesEncryption(key, AES::DEFAULT_KEYLENGTH);
    CTR_Mode_ExternalCipher::Encryption ctrEncryption(aesEncryption, iv);

    vector<unsigned char> encrypted;
    StreamTransformationFilter stfEncryptor(
        ctrEncryption,
        new VectorSink(encrypted),
        BlockPaddingSchemeDef::NO_PADDING);

    stfEncryptor.Put((const byte*)data.data(), data.size());
    stfEncryptor.MessageEnd();

    vector<unsigned char> output(encrypted.size() + 16);

    for (size_t i = 0; i < 16; i++)
        output[i] = iv[i];
    for (size_t i = 0; i < encrypted.size(); i++)
        output[16 + i] = encrypted[i];

    return output;
}

vector<unsigned char> AESCrypt::decrypt(vector<unsigned char> key_, vector<unsigned char> data_)
{    
    byte key[AES::DEFAULT_KEYLENGTH], iv[AES::BLOCKSIZE];
    for (size_t i = 0; i < AES::DEFAULT_KEYLENGTH; i++)
        key[i] = key_[i];
    for (size_t i = 0; i < 16; i++)
        iv[i] = data_[i];

    vector<unsigned char> data(data_.size() - 16);
    for (size_t i = 0; i < data.size(); i++)
    {
        data[i] = data_[16 + i];
    }

    AES::Decryption aesDecryption(key, AES::DEFAULT_KEYLENGTH);
    CTR_Mode_ExternalCipher::Decryption ctrDecryption(aesDecryption, iv);

    vector<unsigned char> decrypted;
    StreamTransformationFilter stfDecryptor(
        ctrDecryption,
        new VectorSink(decrypted),
        BlockPaddingSchemeDef::NO_PADDING);

    stfDecryptor.Put((const byte*)data.data(), data.size()); //this is where it crashes
    stfDecryptor.MessageEnd();

    return decrypted;

}

Here is a picture off CallStack:

在此处输入图片说明

This is the line where it stopps inside the Crypto++ Libary:

CRYPTOPP_ASSERT(m_cipher->IsForwardTransformation());

This is the line where it stopps inside the CryptoPP Libary:

 CRYPTOPP_ASSERT(m_cipher->IsForwardTransformation());

Counter mode (CTR) uses the forward transformation for both encryption and decryption. "Forward transformation" means encryption. The keystream is generated by encrypting the IV/counter, and then the keystream is XOR'd with the plaintext or ciphertext.

You should change this:

AES::Decryption aesDecryption(key, AES::DEFAULT_KEYLENGTH);
CTR_Mode_ExternalCipher::Decryption ctrDecryption(aesDecryption, iv);

To this:

AES::Encryption aesDecryption(key, AES::DEFAULT_KEYLENGTH);
CTR_Mode_ExternalCipher::Decryption ctrDecryption(aesDecryption, iv);

Notice counter mode decryption uses an encryptor.

The wiki has an example of counter mode, and we included this Q&A as part of the external cipher example. Also see CTR Mode on the Crypto++ wiki.


Here's the reproducer.

$ cat test.cxx
#include "cryptlib.h"
#include "secblock.h"
#include "filters.h"
#include "modes.h"
#include "aes.h"

#include <iostream>
#include <string>

int main (int argc, char* argv[])
{
    using namespace CryptoPP;

    SecByteBlock key(16), iv(16);
    std::memset(key, 0xff, key.size());
    std::memset( iv, 0x88,  iv.size());

    std::string message, encrypted, decrypted;
    message = "Now is the time for all good men "
        "to come to the aide of their country.";

    AES::Encryption aesEncryption(key, key.size());
    CTR_Mode_ExternalCipher::Encryption ctrEncryption(aesEncryption, iv);

    StreamTransformationFilter stfEncryptor(
        ctrEncryption, new StringSink(encrypted));

    stfEncryptor.Put((const byte*)&message[0], message.size());
    stfEncryptor.MessageEnd();

    AES::Encryption aesDecryption(key, key.size());
    CTR_Mode_ExternalCipher::Decryption ctrDecryption(aesDecryption, iv);

    StreamTransformationFilter stfDecryptor(
        ctrDecryption, new StringSink(decrypted));

    stfDecryptor.Put((const byte*)&encrypted[0], encrypted.size());
    stfDecryptor.MessageEnd();

    std::cout << "Message: " << message << std::endl;
    std::cout << "Recovered: " << decrypted << std::endl;

    return 0;
}

And:

$ g++ -g2 -O1 test.cxx ./libcryptopp.a -o test.exe
$ ./test.exe
Message: Now is the time for all good men to come to the aide of their country.
Recovered: Now is the time for all good men to come to the aide of their country.

 StreamTransformationFilter stfEncryptor( ctrEncryption, new VectorSink(encrypted), BlockPaddingSchemeDef::NO_PADDING);

For counter mode, there is no need for BlockPaddingSchemeDef::NO_PADDING . It is the default for counter mode.

Though not obvious, it is stated in the docs at BlockPaddingScheme in the manual:

DEFAULT_PADDING means PKCS_PADDING if cipher.MandatoryBlockSize() > 1 && cipher.MinLastBlockSize() == 0, which holds for ECB or CBC mode. Otherwise, NO_PADDING for modes like OFB, CFB, CTR, CBC-CTS.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM