adriank
adriank

Reputation: 183

Different results when encrypting with Common Crypto and Crypto++ with AES

I get different results when encrypting the same file (binary data) with the same key with Apple's Common Crypto and Crypto++. The algorithm I'm using is AES.

Here's the code in Objective C using Common Crypto:

void FileUtil::writeToFileEncrypt(string fileName, const void *data, int size, string key, int *sizeOut)
{
    int numBytesEncrypted = 0;
    char keyPtr[kCCKeySizeAES256+1];

    if (key.length() > 32)
    {
        key = key.substr(0, 32);
    }

    memcpy(keyPtr, key.c_str(), sizeof(keyPtr));

    if (key.length() < 32)
    {
        for (int i = key.length(); i < 32; i++)
        {
            keyPtr[i] = '0';
        }
    }

    size_t bufferSize = size + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                      keyPtr, kCCKeySizeAES256,
                                      NULL /* initialization vector (optional) */,
                                      data, size, /* input */
                                      buffer, bufferSize, /* output */
                                      &numBytesEncrypted);

    if (cryptStatus == kCCSuccess) {
        cout << "encrypt success" << endl;
    }

    ofstream myfile;
    myfile.open (fileName.c_str(), ios::out | ios::binary);
    myfile.write((const char *)buffer, numBytesEncrypted);
    myfile.close();

    free(buffer);

    *sizeOut = numBytesEncrypted;
}

Here's the code in C++ using Crypto++

void EncryptUtils::encrypt(string fileName, unsigned char *chars, string key, int *length, int dataLength)
{
    unsigned char *iv = (unsigned char *)"0000000000000000";

    char keyPtr[32 + 1];

    if (key.length() > 32)
    {
        key = key.substr(0, 32);
    }

    memcpy(keyPtr, key.c_str(), sizeof(keyPtr));

    if (key.length() < 32)
    {
        for (int i = key.length(); i < 32; i++)
        {
            keyPtr[i] = '0';
        }
    }
    keyPtr[32] = '\0';

    CryptoPP::AES::Encryption aesEncryption((unsigned char *)keyPtr, 32);
    CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv);

    int newBufSize = (sizeof(unsigned char *) * dataLength) + 32;
    unsigned char *newBuf = (unsigned char *)malloc(newBufSize);
    CryptoPP::ArraySink *arraySink = new CryptoPP::ArraySink(newBuf, newBufSize);

    CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, arraySink, CryptoPP::StreamTransformationFilter::PKCS_PADDING);
    stfEncryptor.Put(reinterpret_cast<const unsigned char*>(chars), (unsigned int)dataLength);
    stfEncryptor.MessageEnd();

    *length = arraySink->TotalPutLength();

    ofstream myfile;
    myfile.open (fileName.c_str(), ios::out | ios::binary);
    myfile.write((const char *)newBuf, *length);
    myfile.close();
}

I need to get both of them to produce the same results. Is there something I overlooked?

Upvotes: 2

Views: 1254

Answers (2)

jww
jww

Reputation: 102205

This is wrong:

memcpy(keyPtr, key.c_str(), sizeof(keyPtr));

It tries to copy 33 bytes. I think you need something like:

size_t ksize = std::min(sizeof(keyPtr), key.size());
assert(ksize == 16 || ksize == 24 || ksize == 32);

-----

In general, the design is wrong:

keyPtr[32] = '\0';

Keys are binary, and not ASCII strings. If you are using passwords, then its still wrong since you should be using a KDF to digest the password into a suitable key.

-----

The common Crypto code lacks an IV and may not use the same padding as the Crypto++ example.

-----

The use of AES appears to be wrong. It appears your are operating the cipher in ECB mode, so its only providing confidentiality if the data is less than a block size. Its subject to reordering and manipulation, so its not providing authenticity assurances. You should use an Authenticated Encryption like EAX, GCM or CCM mode.

Since Common Crypto is lame (it does not provide authenticated encryption modes) and Crypto++ provides everything you need (and it runs nearly everywhere), you should use Crypto++ and an authenticated encryption mode.

Upvotes: 0

zaph
zaph

Reputation: 112857

  1. The "Objective-C" version is not written in Objective-C, it is in C++. the actual encryption used CCCrypt which is plain "C".

  2. The "Objective-C" version has no iv supplied so it defaults to all zeros. The C++ version supplies an iv of ASCII "0" characters, that is not the same as all zero data.This is probably the error.

  3. Provide input and output hex data dumps for each including key, iv, data in and data out immediately prior to and after the encryption calls.

Upvotes: 1

Related Questions