tags:

views:

260

answers:

3

I was bored and wrote a wrapper around openSSL to do AES encryption with less work. If I do it like this: http://pastebin.com/V1eqz4jp (ivec = 0)
Everything works fine, but the default ivec is all 0's, which has some security problems. Since I'm passing the data back as a string anyway, I figured, why not generate a random ivec and stick it to the front, the take it back off when I decrypt the string? For some reason it doesn't work though.

Well actually, it almost works. It seems to decrypt the middle of the string, but not the beginning or end:

String is: 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
Encrypting..
���l%%1u���B!
�����`pN)�ɶ���[l�ӏ��{�Q�?�2�/�HԵ�y"�=Z�Cu����l%%1u���B!

Decrypting..
String is: �%���G*�5J�0��0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF

I honestly have no idea what's going wrong. Maybe some stupid mistake, or maybe I'm missing something about AES?

Here's the code: (Edited to incorporate Steve Jessop's solution to my first problem)

/*!
 * Simple AES
 * Brendan Long
 * March 29, 2010
 * 
 * Simplified encryption and decryption using OpenSSL's AES library.
 * Remember to compile with -lcrypto and link against the library
 * g++ (your stuff) -lcrypto simpleAes.cpp (or simpleAes.o)
 *
 * Implementation note: Using the default ivec (0) is not secure. For
 *                      the full security that AES offers, use a different
 *                      ivec each time (it does not need to be secret,
 *                      just different.
 *
 * This code is released into the public domain. Yada yada..
 * Read this for details: http://creativecommons.org/licenses/publicdomain/
 *
 * If for some reason public domain isn't good enough, you may use, alter,
 * distribute or do anything else you want with this code with no restrictions.
 */

#include <openssl/aes.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>

bool seed = true;

/*!
 * Encrypts a string using AES with a 256 bit key
 * Note: If the key is less than 32 bytes, it will be null padded.
 *       If the key is greater than 32 bytes, it will be truncated
 * \param in The string to encrypt
 * \param key The key to encrypt with
 * \return The encrypted data
 */
std::string aes_encrypt(std::string in, std::string key){

    // Seed the random number generator once
    if(seed){
        srand( (unsigned int) time(NULL));
        seed = false;
    }

    // Generate a random ivec
    unsigned char ivec[16];
    for(int i=0; i<16; i++){
        ivec[i] = (unsigned char) rand();
    }

     // Round up to AES_BLOCK_SIZE
    size_t textLength = ((in.length() / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;

    // Always pad the key to 32 bits.. because we can
    if(key.length() < 32){
        key.append(32 - key.length(), '\0');
    }

    // Get some space ready for the output
    unsigned char *output = new unsigned char[textLength];

    // Generate a key
    AES_KEY *aesKey = new AES_KEY;
    AES_set_encrypt_key((unsigned char*)key.c_str(), 256, aesKey);

    // Encrypt the data
    AES_cbc_encrypt((unsigned char*)in.c_str(), output, in.length() + 1, aesKey, ivec, AES_ENCRYPT);

    // Make the data into a string
    std::string ret((char*) output, textLength);

    // Add the ivec to the front
    ret = std::string((char*)ivec, 16) + ret;

    // Clean up
    delete output;
    delete aesKey;

    return ret;
}

/*!
 * Decrypts a string using AES with a 256 bit key
 * Note: If the key is less than 32 bytes, it will be null padded.
 *       If the key is greater than 32 bytes, it will be truncated
 * \param in The string to decrypt
 * \param key The key to decrypt with
 * \return The decrypted data
 */
std::string aes_decrypt(std::string in, std::string key){

    // Get the ivec from the front
    unsigned char ivec[16];
    for(int i=0;i<16; i++){
        ivec[i] = in[i];
    }

    in = in.substr(16);

    // Always pad the key to 32 bits.. because we can
    if(key.length() < 32){
        key.append(32 - key.length(), '\0');
    }

    // Create some space for output
    unsigned char *output = new unsigned char[in.length()]; 

    // Generate a key
    AES_KEY *aesKey = new AES_KEY;
    AES_set_decrypt_key((unsigned char*)key.c_str(), 256, aesKey); // key length is in bits, so 32 * 8 = 256

    // Decrypt the data
    AES_cbc_encrypt((unsigned char*)in.c_str(), output, in.length(), aesKey, ivec, AES_DECRYPT);

    // Make the output into a string
    std::string ret((char*) output);

    // Clean up
    delete output;
    delete aesKey;

    return ret;
}
+1  A: 

This line is wrong:

std::string ret((char*) output);

The decrypted data doesn't have a nul terminator, since you encrypted in.length() bytes. That accounts for the garbage at the end, but not the garbage at the beginning. There may be other problems as well.

Steve Jessop
Ah thanks. That's half the problem indeed :)
Brendan Long
A: 

In general, you cannot treat the output of the encryption stage as a string, unless you perform an additional step, such as Base 64 encoding the output. Any output byte could be a nul.

Damien_The_Unbeliever
Yeah, but I'm assuming that my encrypt() function was used to encrypt the data, and my encrypt() function always uses a null terminated string.
Brendan Long
Your encrypt function uses a null terminated string - but *any* output byte from the encryption process could be null. Any byte of your ivec buffer could be null. You need to stop thinking of these byte arrays as implicitly convertable to sensible string values, unless you encode them appropriately
Damien_The_Unbeliever
A std::string can contain nul bytes without problem. That's what the (char*, size_t) constructor is for, which the questioner's code (mostly) uses.
Steve Jessop
A: 

A friend of mine figured out the problem. I'm doing this:

  1. Generate random number and store it in ivec
  2. Encrypt data with ivec
  3. Append ivec to beginning of output data

The problem is that step 2 changes the contents of ivec. I was basically storing random numbers at the beginning of my string. The solution was to add this:

unsigned char ivec[16];
// set ivec to random numbers
std::string ivecString((char*) ivec, 16);
// encrypt data
return ivecString + encryptedData;
Brendan Long