C++ AES GCM Authenticated Encryption


GCM (or http://en.wikipedia.org/wiki/Galois/Counter_Mode) is a mode of operation for AES encryption which provides integrity through a MAC (message authentication code) as well as confidentiality. The following is example code for simple case of encrypting a string with openssl.

In this example, the first 16 bytes of the encrypted string output contains the GMAC tag, the next 16 contains the IV (initialization vector) used to encrypt the string, and the remaining bytes at the ciphertext. We are using /dev/urandom as the random number generator.
//g++ main.cpp -lcrypto
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <string>
#include <sstream>
#include <vector>
#include <iostream>
 
using std::string;
using std::vector;
using std::cout;
using std::endl;
 
void aes_init()
{
    static int init=0;
    if (init==0)
    {
        EVP_CIPHER_CTX e_ctx, d_ctx;
 
        //initialize openssl ciphers
        OpenSSL_add_all_ciphers();
 
        //initialize random number generator (for IVs)
        int rv = RAND_load_file("/dev/urandom", 32);
    }
}
 
std::vector<unsigned char> aes_128_gcm_encrypt(std::string plaintext, std::string key)
{
    aes_init();
 
    size_t enc_length = plaintext.length()*3;
    std::vector<unsigned char> output;
    output.resize(enc_length,'\0');
 
    unsigned char tag[AES_BLOCK_SIZE];
    unsigned char iv[AES_BLOCK_SIZE];
    RAND_bytes(iv, sizeof(iv));
    std::copy( iv, iv+16, output.begin()+16);
 
    int actual_size=0, final_size=0;
    EVP_CIPHER_CTX* e_ctx = EVP_CIPHER_CTX_new();
    //EVP_CIPHER_CTX_ctrl(e_ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL);
    EVP_EncryptInit(e_ctx, EVP_aes_128_gcm(), (const unsigned char*)key.c_str(), iv);
    EVP_EncryptUpdate(e_ctx, &output[32], &actual_size, (const unsigned char*)plaintext.data(), plaintext.length() );
    EVP_EncryptFinal(e_ctx, &output[32+actual_size], &final_size);
    EVP_CIPHER_CTX_ctrl(e_ctx, EVP_CTRL_GCM_GET_TAG, 16, tag);
    std::copy( tag, tag+16, output.begin() );
    std::copy( iv, iv+16, output.begin()+16);
    output.resize(32 + actual_size+final_size);
    EVP_CIPHER_CTX_free(e_ctx);
    return output;
}
 
std::string aes_128_gcm_decrypt(std::vector<unsigned char> ciphertext, std::string key)
{
    aes_init();
 
    unsigned char tag[AES_BLOCK_SIZE];
    unsigned char iv[AES_BLOCK_SIZE];
    std::copy( ciphertext.begin(),    ciphertext.begin()+16, tag);
    std::copy( ciphertext.begin()+16, ciphertext.begin()+32, iv);
    std::vector<unsigned char> plaintext; plaintext.resize(ciphertext.size(), '\0');
 
    int actual_size=0, final_size=0;
    EVP_CIPHER_CTX *d_ctx = EVP_CIPHER_CTX_new();
    EVP_DecryptInit(d_ctx, EVP_aes_128_gcm(), (const unsigned char*)key.c_str(), iv);
    EVP_DecryptUpdate(d_ctx, &plaintext[0], &actual_size, &ciphertext[32], ciphertext.size()-32 );
    EVP_CIPHER_CTX_ctrl(d_ctx, EVP_CTRL_GCM_SET_TAG, 16, tag);
    EVP_DecryptFinal(d_ctx, &plaintext[actual_size], &final_size);
    EVP_CIPHER_CTX_free(d_ctx);
    plaintext.resize(actual_size + final_size, '\0');
 
    return string(plaintext.begin(),plaintext.end());
}
 
int main(int argc, char **argv)
{
    aes_init();
 
    //create a sample key
    unsigned char key_bytes[16];
    RAND_bytes(key_bytes, sizeof(key_bytes));
    string key = string((char *)key_bytes, sizeof(key_bytes));
 
    //text to encrypt
    string plaintext= "elephants in space";
    cout << plaintext << endl;
 
    //encrypt
    vector<unsigned char> ciphertext = aes_128_gcm_encrypt(plaintext, key);
 
    //output
    static const char *chars="0123456789ABCDEF";
    for(int i=0; i<ciphertext.size(); i++)
    {
        cout << chars[ciphertext[i]/16];
        cout << chars[ciphertext[i]%16];
    }
    cout << endl;
 
    //decrypt
    string out = aes_128_gcm_decrypt(ciphertext, key);
    cout << out << endl;
}
//http://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption