/*
 * Simple test program to compare output of XTS vs GCM with OpenSSL EVP APIs
 */

#include <stdio.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/crypto.h>


static const unsigned char aes_key[] = {
    0xee, 0xbc, 0x1f, 0x57, 0x48, 0x7f, 0x51, 0x92, 0x1c, 0x04, 0x65, 0x66,
    0x5f, 0x8a, 0xe6, 0xd1, 0x65, 0x8b, 0xb2, 0x6d, 0xe6, 0xf8, 0xa0, 0x69,
    0xa3, 0x52, 0x02, 0x93, 0xa5, 0x72, 0x07, 0x8f,
    0xee, 0xbc, 0x1f, 0x57, 0x48, 0x7f, 0x51, 0x92, 0x1c, 0x04, 0x65, 0x66,
    0x5f, 0x8a, 0xe6, 0xd1, 0x65, 0x8b, 0xb2, 0x6d, 0xe6, 0xf8, 0xa0, 0x69,
    0xa3, 0x52, 0x02, 0x93, 0xa5, 0x72, 0x97, 0x9f
};

static const unsigned char aes_iv[] = {
    0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, 0xee, 0xd0, 0x66, 0x84
};

void EncryptionTest(EVP_CIPHER_CTX *ctx, int blocksize, unsigned char *data, int dsize)
{
    int next_op_size = 0;
    int remains = dsize;
    int tmp_len;
    
    unsigned char buffer[1024];
    
    unsigned char *ctcurrent = buffer;
    unsigned char *ptcurrent = data;
    
    
    while (remains > 0) {
        
        next_op_size = (blocksize < remains ? blocksize : remains);
        
        if (1 != EVP_EncryptUpdate(ctx, ctcurrent, &tmp_len, ptcurrent,
                                   next_op_size))
        {
            printf("Encryption error\n");
            return;
        }
        
        remains -= blocksize;
        ctcurrent += tmp_len;
        ptcurrent += blocksize;
    }
    
    EVP_EncryptFinal_ex(ctx, &buffer[dsize], &tmp_len);
    
    BIO_dump_fp(stdout, (char *)buffer, dsize);
    printf("\n");
}

void EncryptWithGCM(int blocksize, unsigned char *data, int dsize)
{
    EVP_CIPHER_CTX *ctx;
    ctx = EVP_CIPHER_CTX_new();
    
    // Setup GCM mode
    if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, aes_key, aes_iv))
    {
        printf("EncryptInit error\n");
        return;
    }
    
    EncryptionTest(ctx, blocksize, data, dsize);
    
    EVP_CIPHER_CTX_free(ctx);
    
}

void EncryptWithXTS(int blocksize, unsigned char *data, int dsize)
{
    EVP_CIPHER_CTX *ctx;
    ctx = EVP_CIPHER_CTX_new();
    
    // Setup XTS mode
    if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_xts(), NULL, aes_key, aes_iv))
    {
        printf("EncryptInit error\n");
        return;
    }
    
    EncryptionTest(ctx, blocksize, data, dsize);
    
    // Let's free the OpenSSL CTX structure
    EVP_CIPHER_CTX_free(ctx);
    
}

int main(int argc, char **argv)
{

    
    char * text =
    // 128bits per row
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF"
    "0123456789ABCDEF";
    
    int textlen = strlen(text);
   
    printf("XTS in one block\n");
    EncryptWithXTS(textlen, (unsigned char *)text, textlen);
    printf("XTS in 16 byte blocks\n");
    EncryptWithXTS(16, (unsigned char *)text, textlen);
    printf("GCM in one block\n");
    EncryptWithGCM(textlen, (unsigned char *)text, textlen);
    printf("GCM in 16 byte blocks\n");
    EncryptWithGCM(16, (unsigned char *)text, textlen);
    
    return 0;
    
}
