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                       openssl-dev@openssl.org
> Automated List Manager                           majord...@openssl.org
> .
>

/*
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>>

Reply via email to