#include "xs/openssl.h"

MODULE = Crypt::Keyczar		PACKAGE = Crypt::Keyczar::AesEngine


Crypt::Keyczar::AesEngine
new(class, key)
	SV *class
	SV *key
	CODE:
	{
	    STRLEN l;
	    unsigned char *k;
	
	    k = (unsigned char *)SvPV(key, l);
	    PERL_UNUSED_VAR(class);
	    Newz(0, RETVAL, 1, struct Crypt__Keyczar__AesEngine_class);
	    RETVAL->context = EVP_CIPHER_CTX_new();
	    Newz(0, RETVAL->key, l, unsigned char);
	    Newz(0, RETVAL->iv, 16, unsigned char);

	    memmove(RETVAL->key, k, l);
	    RETVAL->key_length = l*8;
	}
	OUTPUT:
	    RETVAL


void
DESTROY(self)
	Crypt::Keyczar::AesEngine self
	CODE:
	{
	    if (self->context != NULL) {
	        EVP_CIPHER_CTX_free(self->context);
	        self->context = NULL;
	    }
	    if (self->key != NULL) {
	        Safefree(self->key);
	        self->key = NULL;
	    }
	    if (self->iv != NULL) {
	        Safefree(self->iv);
	        self->iv = NULL;
	    }
	    Safefree(self);
	}


SV *
init(self, ...)
	Crypt::Keyczar::AesEngine self
	CODE:
	{
	    SV *iv;

	    if (items > 1) {
	        iv = ST(1);
	        memmove(self->iv, SvPVbyte_nolen(iv), 16);
	    }
	    else {
	        if (RAND_bytes(self->iv, 16) != 1) {
                    crypt__keyczar__util__croak_openssl();
	            /* NOTREACHED */
	        }	
	    }

	    if (self->iv != NULL) {
	        RETVAL = newSVpv((char *)self->iv, 16);
	    }
	    else {
	        RETVAL = newSVpv("", 0);
	    }
	}
	OUTPUT:
	    RETVAL


SV *
encrypt(self, data)
	Crypt::Keyczar::AesEngine self
	SV *data
	CODE:
	{
	    STRLEN in_l;
	    unsigned char *in, *out;
	    int out_l, last_l;
	    const EVP_CIPHER *type;

	    if (self->key_length == 128) {
	        type = EVP_aes_128_cbc();
	    }
	    else if (self->key_length == 192) {
	        type = EVP_aes_192_cbc();
	    }
	    else if (self->key_length == 256) {
	        type = EVP_aes_256_cbc();
	    }
	    else {
	        croak("unsupported key length: %d", self->key_length);
	    }

	    EVP_EncryptInit_ex(self->context, type, NULL, self->key, self->iv);
	    Newz(0, out, SvCUR(data) + EVP_CIPHER_CTX_block_size(self->context), unsigned char);
	    in = (unsigned char *)SvPV(data, in_l);
	    if (!EVP_EncryptUpdate(self->context, out, &out_l, in, in_l)) {
	        RETVAL = NULL;
	        Safefree(out);
	        crypt__keyczar__util__croak_openssl(); 
	        return;	
	    }
	    if (!EVP_EncryptFinal_ex(self->context, out + out_l, &last_l)) {
	        RETVAL = NULL;
	        Safefree(out);
	        crypt__keyczar__util__croak_openssl(); 
	        return;	
	    }
	    RETVAL = newSVpv((char *)out, out_l+last_l);
	    Safefree(out);
	}
	OUTPUT:
	    RETVAL


SV *
decrypt(self, data)
	Crypt::Keyczar::AesEngine self
	SV *data
	CODE:
	{
	    STRLEN in_l;
	    unsigned char *in, *out;
	    int out_l, last_l;
	    const EVP_CIPHER *type;

	    if (self->key_length == 128) {
	        type = EVP_aes_128_cbc();
	    }
	    else if (self->key_length == 192) {
	        type = EVP_aes_192_cbc();
	    }
	    else if (self->key_length == 256) {
	        type = EVP_aes_256_cbc();
	    }
	    else {
	        croak("unsupported key length: %d", self->key_length);
	    }
	
	    EVP_DecryptInit_ex(self->context, type, NULL, self->key, self->iv);
	    in = (unsigned char *)SvPV(data, in_l);
	    Newz(0, out, in_l, unsigned char);
	    if (!EVP_DecryptUpdate(self->context, out, &out_l, in, in_l)) {
	        RETVAL = NULL;
	        Safefree(out);
	        crypt__keyczar__util__croak_openssl(); 
	        return;
	    }
	    if (!EVP_DecryptFinal_ex(self->context, out + out_l, &last_l)) {
	        RETVAL = NULL;
	        Safefree(out);
	        crypt__keyczar__util__croak_openssl(); 
	        return;	
	    }
	    RETVAL = newSVpv((char *)out, out_l+last_l);
	    Safefree(out);
	}
	OUTPUT:
	    RETVAL