#include <stdio.h>
#include <stdlib.h>
#include <string.h>
# include <gnutls/gnutls.h>
# include <gnutls/crypto.h>

static void hexdump(const char *why, unsigned char *start, unsigned int len) {
    printf("dump(%s)\n", why);
    for (unsigned int i = 0; i < len; i++) {
        unsigned char *p = start + i;
        printf("%02x", *p);
        if ((i % 100) == 99)
            printf("\n");
    }
    printf("\n");
}

int
main(void) {
    gnutls_cipher_algorithm_t ALGO = GNUTLS_CIPHER_AES_256_GCM;
        //GNUTLS_CIPHER_SALSA20_256;
        //GNUTLS_CIPHER_AES_256_CBC;
    int key_size = gnutls_cipher_get_key_size(ALGO);
    int tag_size = gnutls_cipher_get_tag_size(ALGO);
    int iv_size = gnutls_cipher_get_iv_size(ALGO);
    int data_size = 16;
    unsigned char *key = calloc(1, key_size);
    unsigned char *data = calloc(1, data_size+tag_size);
    unsigned char *iv = calloc(1, iv_size);
    gnutls_datum_t datum_key;
    gnutls_cipher_hd_t cipher_hd;
    int rc, i;

    printf("keysize=%d, tagsize=%d\n", key_size, tag_size);
    //gnutls_global_init ();
    for (i=0; i<key_size; i++)
        key[i] = 0x01; // key is all-ones
    gnutls_rnd(GNUTLS_RND_KEY, key, key_size);
    datum_key.data = key;
    datum_key.size = key_size;
    for (i=0; i < iv_size; i++)
        iv[i] = 0x02; // IV is all-twos
    
    rc = gnutls_cipher_init(&cipher_hd, ALGO, &datum_key, NULL);
    printf("init: rc=%d\n", rc);

    for (i=0; i<data_size; i++)
        data[i] = 0x03; // data is all-threes
    hexdump("before encrypt", data, data_size+tag_size);

    printf("about to set_iv\n");
    gnutls_cipher_set_iv (cipher_hd, iv, iv_size);
    printf("did set_iv\n");
    rc = gnutls_cipher_encrypt (cipher_hd, data, data_size);
    printf("gnutls_cipher_encrypt: rc=%d\n", rc);
    hexdump("after encrypt", data, data_size+tag_size);
    rc = gnutls_cipher_tag (cipher_hd, data + data_size, tag_size);
    printf("gnutls_cipher_tag: rc=%d\n", rc);
    hexdump("after tag", data, data_size+tag_size);


    if (1) {
        gnutls_cipher_hd_t decrypt_hd;
        unsigned char *new_tag = calloc(1, tag_size);
    
        rc = gnutls_cipher_init(&decrypt_hd, ALGO, &datum_key, NULL);
        printf("decrypt init: rc=%d\n", rc);
        gnutls_cipher_set_iv (decrypt_hd, iv, iv_size);
        rc = gnutls_cipher_decrypt (decrypt_hd, data, data_size);
        hexdump("after decrypt", data, data_size+tag_size);
        gnutls_cipher_tag (decrypt_hd, new_tag, tag_size);
        hexdump("new tag", new_tag, tag_size);
        printf("tag cmp: %d\n", memcmp(data+data_size, new_tag, tag_size));
    }

    return 0;
}
