Agreed, utilizing GCM mode is like solving a puzzle. Some documentation would be helpful. The EVP API doesn't appear to be setup for AEAD ciphers. The attached code shows one way to use the EVP API for AES-GCM mode. Hopefully this helps.
On 03/01/2013 02:12 AM, Leon Brits wrote: > I am trying to add AES-GCM mode to my code which has been working for most > other modes for quite a while now. The mode is given as a parameter and I use > it for GCM mode to switch and do special stuff such as to set the AAD and > get/set the tag for AES-GCM mode. > > In the encipherment function I store the tag at the end of the ciphertext and > return a larger data size. In the decipherment function I automatically > reduce the size by 16 and use the last 16 bytes as the tag to compare. This > will be so documented for this mode in the library header. > > I've followed the advise of these two posting: > http://stackoverflow.com/questions/12153009/openssl-c-example-of-aes-gcm-using-evp-interfaces > and > http://incog-izick.blogspot.in/2011/08/using-openssl-aes-gcm.html > > My problem is that the call to get the tag fails (EVP_CIPHER_CTX_ctrl() > returns 1) in the encipherment function. > > Can anybody shine some light on what my problem may be? (I can post code if > you want, but the referenced links contain good code already) > > I am working on Ubuntu 12.10 which has the following OpenSSL installed: > > $ openssl version -a > > OpenSSL 1.0.1 14 Mar 2012 built on: Tue Aug 21 05:18:48 UTC 2012 platform: > debian-amd64 options: bn(64,64) rc4(16x,int) des(idx,cisc,16,int) > blowfish(idx) compiler: cc -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS > -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -m64 -DL_ENDIAN -DTERMIO -g -O2 > -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security > -Werror=format-security -D_FORTIFY_SOURCE=2 -Wl,-Bsymbolic-functions > -Wl,-z,relro -Wa,--noexecstack -Wall -DOPENSSL_NO_TLS1_2_CLIENT > -DOPENSSL_MAX_TLS1_2_CIPHER_LENGTH=50 -DMD32_REG_T=int -DOPENSSL_IA32_SSE2 > -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM > -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM > -DWHIRLPOOL_ASM -DGHASH_ASM OPENSSLDIR: "/usr/lib/ssl" > > ______________________________________________________________________ > OpenSSL Project http://www.openssl.org > Development Mailing List [email protected] > Automated List Manager [email protected] > . >
/*
Example compiler command:
gcc -g gcmtest.c -L/nobackup/tmp/x1/openssl-1.0.1c -lcrypto
*/
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#define PTLEN 60
#define TAGLEN 8
#define IVLEN 12
#define AADLEN 20
#define KEYLEN 16
static unsigned char Payload[PTLEN] = {
0xd9,0x31,0x32,0x25,0xf8,0x84,0x06,0xe5,
0xa5,0x59,0x09,0xc5,0xaf,0xf5,0x26,0x9a,
0x86,0xa7,0xa9,0x53,0x15,0x34,0xf7,0xda,
0x2e,0x4c,0x30,0x3d,0x8a,0x31,0x8a,0x72,
0x1c,0x3c,0x0c,0x95,0x95,0x68,0x09,0x53,
0x2f,0xcf,0x0e,0x24,0x49,0xa6,0xb5,0x25,
0xb1,0x6a,0xed,0xf5,0xaa,0x0d,0xe6,0x57,
0xba,0x63,0x7b,0x39
};
static unsigned char AAD[AADLEN] = {
0xfe,0xed,0xfa,0xce,0xde,0xad,0xbe,0xef,
0xfe,0xed,0xfa,0xce,0xde,0xad,0xbe,0xef,
0xab,0xad,0xda,0xd2
};
static unsigned char IV[IVLEN] = {
0xca,0xfe,0xba,0xbe,0xfa,0xce,0xdb,0xad,0xde,0xca,0xf8,0x88
};
static unsigned char Key[KEYLEN] = {
0xfe,0xff,0xe9,0x92,0x86,0x65,0x73,0x1c,
0x6d,0x6a,0x8f,0x94,0x67,0x30,0x83,0x08
};
#if 0
static unsigned char ctexpect[68] = {
0x42,0x83,0x1e,0xc2,0x21,0x77,0x74,0x24,
0x4b,0x72,0x21,0xb7,0x84,0xd0,0xd4,0x9c,
0xe3,0xaa,0x21,0x2f,0x2c,0x02,0xa4,0xe0,
0x35,0xc1,0x7e,0x23,0x29,0xac,0xa1,0x2e,
0x21,0xd5,0x14,0xb2,0x54,0x66,0x93,0x1c,
0x7d,0x8f,0x6a,0x5a,0xac,0x84,0xaa,0x05,
0x1b,0xa3,0x0b,0x39,0x6a,0x0a,0xac,0x97,
0x3d,0x58,0xe0,0x91,
/* the last 8 bytes are the tag */
0x5b,0xc9,0x4f,0xbc,0x32,0x21,0xa5,0xdb,
};
#endif
static unsigned char CT[512];
static void dump_hex(char *lbl, unsigned char *c, int len)
{
int i;
printf("\n%s: ", lbl);
for (i=0; i<len; i++) {
printf("%02x ", c[i]);
}
printf("\n");
}
/*
* This is an example program that encrypts and then decrypts
* using AES-128 GCM mode. The OpenSSL 1.0.1c GCM interface is
* primarily setup for TLS, which uses a 12 byte IV and 16 byte
* tag and 13 bytes of AAD. Using alternate values for these
* parameters is more challenging. Hence, this code shows one
* way to use the EVP interface for AES GCM mode when not doing
* TLS. Note: there are probably other ways to skin this cat.
*/
int main (int argc, char *argv[])
{
EVP_CIPHER_CTX ctx;
const EVP_CIPHER *gcm = EVP_aes_128_gcm();
unsigned char *tag;
unsigned char ptbuf[512];
unsigned char tagbuf[16];
unsigned char *ctbuf;
int rv;
EVP_CIPHER_CTX_init(&ctx);
/*
* Init the cipher and set the key
*/
EVP_CipherInit_ex(&ctx, gcm, NULL, Key, NULL, 1);
/*
* Set the IV
* Note: Be sure to use a value of 0 when invoking with EVP_CTRL_GCM_IV_GEN,
* otherwise the data pointed to by the IV paramter will be modified by
* libcrypto.
*/
EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, IVLEN, 0);
EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IV_FIXED, -1, IV);
if(!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_IV_GEN, 0, IV)) {
printf("\nFailed to set IV");;
exit(2);
}
/*
* Process the AAD
*/
rv = EVP_Cipher(&ctx, NULL, AAD, AADLEN);
if (rv != AADLEN) {
printf("\nSet AAD rv=%d\n", rv);
exit(2);
}
/*
* Process the plaintext
*/
EVP_Cipher(&ctx, CT, Payload, PTLEN);
dump_hex("CT", CT, PTLEN);
/*
* This is goofy, but we need to invoke EVP_Cipher again to calculate the tag
*/
EVP_Cipher(&ctx, NULL, NULL, 0);
/*
* OK, now that the tag has been calculated, get the TAG and display it.
* A real application would typically include the tag along with the
* ciphertext when transmitting to a peer.
*/
EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, TAGLEN, tagbuf);
dump_hex("TAG", tagbuf, TAGLEN);
/*
* Note, if you fail to cleanup, there will be a memory leak.
*/
EVP_CIPHER_CTX_cleanup(&ctx);
/************************************************************************************
************************************************************************************
************************************************************************************
* OK, now let's decrypt and see if the original plaintext matches
************************************************************************************
************************************************************************************
************************************************************************************/
tag = tagbuf;
ctbuf = CT;
/*
* Note that this pretty much just does a memory clear of the structure.
* Because were doing another init on the same context. EVP_CIPHER_CTX_cleanup()
* must be called above. Otherwise we would have a memory leak.
*/
EVP_CIPHER_CTX_init(&ctx);
/*
* Init the cipher and set the key
*/
EVP_CipherInit_ex(&ctx, gcm, NULL, Key, NULL, 0);
/*
* Set the IV
*/
EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, IVLEN, 0);
EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IV_FIXED, -1, IV);
if(!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_IV_GEN, 0, IV)) {
printf("\nFailed to set IV");;
exit(2);
}
/*
* Set dummy tag before processing AAD. Otherwise the AAD can
* not be processed.
*/
EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, TAGLEN, ptbuf);
/*
* Process the AAD
*/
rv = EVP_Cipher(&ctx, NULL, AAD, AADLEN);
if (rv != AADLEN) {
printf("\nSet AAD rv=%d\n", rv);
exit(2);
}
/*
* Set the tag when decrypting
*/
EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, TAGLEN, tag);
/*
* Decrypt the ciphertext
*/
EVP_Cipher(&ctx, ptbuf, ctbuf, PTLEN);
/*
* Check the tag
*/
rv = EVP_Cipher(&ctx, NULL, NULL, 0);
if (rv) {
printf("\nGCM decrypt failed due to tag mismatch (%d)\n", rv);
exit(2);
}
/*
* The tag was good, it's save to use the plaintext.
*/
dump_hex("PT2", ptbuf, PTLEN);
/*
* Cleanup again to avoid memory leak
*/
EVP_CIPHER_CTX_cleanup(&ctx);
/*
* Compare to original plaintext to see if the test passed
*/
if (!memcmp(ptbuf, Payload, PTLEN)) {
printf("\nGCM works!!!\n");
} else {
printf("\nGCM failed!!! Original plaintext mismatch\n");
}
}
<<attachment: foleyj.vcf>>
