[gnutls-devel] gnutls_cipher_decrypt2() is broken for AES-GCM

William McGovern will at thaiglish.com
Sat Jan 26 18:40:23 CET 2013


Hello,

It seems that gnutls_cipher_decrypt2() does not work for the AES-GCM
ciphers and does not generate any data in the output buffer or return
any error code. In-place decryption of the same ciphertext with
gnutls_cipher_decrypt() works correctly.

This is seen with GnuTLS 3.1.6 on Mac OS X 10.8.2 as installed
by homebrew on platforms with and without AES-NI support.

I believe my code is correct but let me know if I missed anything :-)

- Will

Here is a test case that illustrates the defect:

/*
 * Test case illustrating bug in gnutls_cipher_decrypt2() with AES-GCM.
 *
 * VERSION: GnuTLS 3.1.6
 * 
 * DEFECT: The gnutls_cipher_decrypt2() function does not correctly decrypt
 *         data for the GNUTLS_CIPHER_AES_[128|256]_GCM
 *         ciphers. The output buffer for the decrypted data is not modified.
 *
 *  NOTES: In-place decryption using gnutls_cipher_decrypt() on the same
 *         ciphertext generated by gnutls_cipher_encrypt2() correctly
 *         decrypts the data in-place. Also, switching the cipher to CBC
 *         mode (and commenting out the AAD and AuthTag calls) shows that
 *         gnutls_cipher_decrypt2() works for CBC. It appears that the
 *         defect is isolated to the GCM mode with or without AES-NI.
 *
 *   ENVS: Mac OS X 10.8.2 : Intel Core i7-2600 (AES-NI)
 *         Mac OS X 10.8.2 : Intel Xeon X5482   (No AES-NI)
 *
 *  BUILD: cc -Wall -o test_case test_case.c -lgnutls
 *    RUN: ./test_case
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>

#define CIPHER GNUTLS_CIPHER_AES_128_GCM

#define KEY_LEN   32
#define IV_LEN    12
#define AAD_LEN   64
#define DATA_LEN  1024
#define TAG_LEN   16

int
main(void)
{
    gnutls_cipher_hd_t h;
    gnutls_datum_t     keyDatum, ivDatum;
    void              *key, *iv, *aad, *pt1, *pt2, *ct, *tag1, *tag2;
    int                rc;

    key  = malloc(KEY_LEN);
    iv   = malloc(IV_LEN);
    aad  = malloc(AAD_LEN);
    pt1  = malloc(DATA_LEN);
    pt2  = malloc(DATA_LEN);
    ct   = malloc(DATA_LEN);
    tag1 = malloc(TAG_LEN);
    tag2 = malloc(TAG_LEN);
    assert(key  != NULL);
    assert(iv   != NULL);
    assert(aad  != NULL);
    assert(pt1  != NULL);
    assert(pt2  != NULL);
    assert(ct   != NULL);
    assert(tag1 != NULL);
    assert(tag2 != NULL);

    /* Construct datums for gnutls_cipher_init(). */
    keyDatum.data = key;
    keyDatum.size = KEY_LEN;
    ivDatum.data  = iv;
    ivDatum.size  = IV_LEN;

     /* Initialize input buffers (random) and zero output buffers. */
    gnutls_global_init();
    gnutls_rnd(GNUTLS_RND_RANDOM, key, KEY_LEN);
    gnutls_rnd(GNUTLS_RND_RANDOM, iv,  IV_LEN);
    gnutls_rnd(GNUTLS_RND_RANDOM, aad, AAD_LEN);
    gnutls_rnd(GNUTLS_RND_RANDOM, pt1, DATA_LEN);
    memset(pt2,  0, DATA_LEN);
    memset(tag1, 0, TAG_LEN);
    memset(tag2, 0, TAG_LEN);

    /* Encrypt data from pt1 -> ct */
    rc = gnutls_cipher_init(&h, CIPHER, &keyDatum, &ivDatum);
    assert(rc == 0);
    rc = gnutls_cipher_add_auth(h, aad, AAD_LEN);
    assert(rc == 0);
    rc = gnutls_cipher_encrypt2(h, pt1, DATA_LEN, ct, DATA_LEN);
    assert(rc == 0);
    rc = gnutls_cipher_tag(h, tag1, TAG_LEN);
    assert(rc == 0);
    gnutls_cipher_deinit(h);

    /* Decrypt data from ct -> pt2 */
    rc = gnutls_cipher_init(&h, CIPHER, &keyDatum, &ivDatum);
    assert(rc == 0);
    rc = gnutls_cipher_add_auth(h, aad, AAD_LEN);
    assert(rc == 0);
    rc = gnutls_cipher_decrypt2(h, ct, DATA_LEN, pt2, DATA_LEN);
    assert(rc == 0);
    rc = gnutls_cipher_tag(h, tag2, TAG_LEN);
    assert(rc == 0);
    gnutls_cipher_deinit(h);

    /* Verify decrypted data matches original plaintext (pt2 == pt1). */
    if (memcmp(pt1, pt2, DATA_LEN) != 0) {
        fprintf(stderr, "ERROR: Out-of-place decrypted data mismatch\n");
    } else {
        fprintf(stderr, "OK: Out-of-place decrypted data matches\n");
    }
    /* Verify authentication tags match. */
    if (memcmp(tag1, tag2, TAG_LEN) != 0) {
        fprintf(stderr, "ERROR: Out-of-place authentication tag mismatch\n");
    } else {
        fprintf(stderr, "OK: Out-of-place authentication tags match\n");
    }

    /* Decrypt data in-place in ct. */
    memset(tag2, 0, TAG_LEN);
    rc = gnutls_cipher_init(&h, CIPHER, &keyDatum, &ivDatum);
    assert(rc == 0);
    rc = gnutls_cipher_add_auth(h, aad, AAD_LEN);
    assert(rc == 0);
    rc = gnutls_cipher_decrypt(h, ct, DATA_LEN);
    assert(rc == 0);
    rc = gnutls_cipher_tag(h, tag2, TAG_LEN);
    assert(rc == 0);
    gnutls_cipher_deinit(h);

    /* Verify decrypted data matches original plaintext (pt2 == pt1). */
    if (memcmp(pt1, ct, DATA_LEN) != 0) {
        fprintf(stderr, "ERROR: In-place decrypted data mismatch\n");
    } else {
        fprintf(stderr, "OK: In-place decrypted data matches\n");
    }
 
    /* Verify authentication tags match. */
    if (memcmp(tag1, tag2, TAG_LEN) != 0) {
        fprintf(stderr, "ERROR: In-place authentication tag mismatch\n");
    } else {
        fprintf(stderr, "OK: In-place authentication tags match\n");
    }
    return 0;
}




More information about the Gnutls-devel mailing list