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_Decryptioncode snippets are licensed under Creative Commons CC-By-SA 3.0 (unless otherwise specified)
|