#include <stdio.h>
#include <string.h>

#undef OPENSSL_NO_DES
#include "des.h"
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/bn.h>
//#include "openssl/engine.h"
#include "openssl-0.9.8/include/openssl/engine.h"
#include <openssl/ossl_typ.h>
#define CRYPTO_PARALLEL_LIB_NAME "des-ecb"
//#include "rsaref_err.c"


/*****************************************************************************
 *** Function declarations and global variable definitions                 ***
 *****************************************************************************/

/*****************************************************************************
 * Constants used when creating the ENGINE
 **/
static const char *engine_desecb_id = "des-ecb";
static const char *engine_desecb_name = "des-ecb engine support";

/*****************************************************************************
 * Functions to handle the engine
 **/
static int desecb_destroy(ENGINE *e);
static int desecb_init(ENGINE *e);
static int desecb_finish(ENGINE *e);
#if 0
static int desecb_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)()); 
#endif


/*****************************************************************************
 * Symetric cipher and digest function registrars
 **/
static int desecb_ciphers(ENGINE *e, const EVP_CIPHER **cipher, const int **nids, int nid);

static int desecb_cipher_nids[] = { NID_des_ecb, 0 };
	
/*****************************************************************************
 * DES functions
 **/
static int des_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc);
static int des_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr);

//static int cipher_des_ecb_init(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc);
static int cipher_des_ecb_code(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, unsigned int inl);
//static int cipher_des_ecb_clean(EVP_CIPHER_CTX *);

/*****************************************************************************
 * Our DES ciphers
 **/
static const EVP_CIPHER cipher_des_ecb =
	{
	NID_des_ecb,
	8, 8, 8,
	64 | 0x200,
	des_init_key,//cipher_des_ecb_init,
	cipher_des_ecb_code,
	NULL,//cipher_des_ecb_clean,
	sizeof(DES_key_schedule),
	EVP_CIPHER_set_asn1_iv,
	EVP_CIPHER_get_asn1_iv,
	des_ctrl,
	NULL
	};
/*****************************************************************************
 * Functions to handle the engine
 **/

static int bind_desecb(ENGINE *e)
	{
	
	if(!ENGINE_set_id(e, engine_desecb_id)
		|| !ENGINE_set_name(e, engine_desecb_name)
		|| !ENGINE_set_ciphers(e, desecb_ciphers)
		|| !ENGINE_set_destroy_function(e, desecb_destroy)
		|| !ENGINE_set_init_function(e, desecb_init)
		|| !ENGINE_set_finish_function(e, desecb_finish)
		/* || !ENGINE_set_ctrl_function(e, rsaref_ctrl) */
		/* || !ENGINE_set_cmd_defns(e, rsaref_cmd_defns) */)
		return 0;

	/* Ensure the rsaref error handling is set up */
	//ERR_load_RSAREF_strings();
	return 1;
	}

#ifndef ENGINE_STATIC_SUPPORT
static int bind_helper(ENGINE *e, const char *id)
	{
	if(id && (strcmp(id, engine_desecb_id) != 0))
		return 0;
	if(!bind_desecb(e))
		return 0;
	return 1;
	}       
IMPLEMENT_DYNAMIC_CHECK_FN()
IMPLEMENT_DYNAMIC_BIND_FN(bind_helper)
#else
static ENGINE *engine_desecb(void)
	{
	ENGINE *ret = ENGINE_new();
	if(!ret)
		return NULL;
	if(!bind_desecb(ret))
		{
		ENGINE_free(ret);
		return NULL;
		}
	return ret;
	}

void ENGINE_load_desecb(void)
	{
	/* Copied from eng_[openssl|dyn].c */
	ENGINE *toadd = engine_desecb();
	if(!toadd) return;
	ENGINE_add(toadd);
	ENGINE_free(toadd);
	//ERR_clear_error();
	}
#endif

/* Initiator which is only present to make sure this engine looks available */
static int desecb_init(ENGINE *e)
	{
	return 1;
	}

/* Finisher which is only present to make sure this engine looks available */
static int desecb_finish(ENGINE *e)
	{
	return 1;
	}

/* Destructor (complements the "ENGINE_ncipher()" constructor) */
static int desecb_destroy(ENGINE *e)
	{
	//ERR_unload_RSAREF_strings();
	return 1;
	}
/*****************************************************************************
 * Symetric cipher and digest function registrars
 **/
static int desecb_ciphers(ENGINE *e, const EVP_CIPHER **cipher, const int **nids, int nid)
	{
	int ok = 1;
	if(!cipher)
		{
		/* We are returning a list of supported nids */
		*nids = desecb_cipher_nids;
		return (sizeof(desecb_cipher_nids)-1)/sizeof(desecb_cipher_nids[0]);
		}
	/* We are being asked for a specific cipher */
	switch (nid)
		{
	case NID_des_ecb:
		*cipher = &cipher_des_ecb; break;
	default:
		ok = 0;
		*cipher = NULL;
		break;
		}
	return ok;
	}

/*****************************************************************************
 * DES functions
 **/
 static int des_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc)
	{
	DES_cblock *deskey = (DES_cblock *)key;
#ifdef EVP_CHECK_DES_KEY
	if(DES_set_key_checked(deskey,ctx->cipher_data) != 0)
		return 0;
#else
	DES_set_key_unchecked(deskey,ctx->cipher_data);
#endif
	return 1;
	}

static int des_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
	{
	
	switch(type)
		{
	case 0x6:
		if (RAND_bytes(ptr, 8) <= 0)
			return 0;
		DES_set_odd_parity((DES_cblock *)ptr);
		return 1;

	default:
		return -1;
		}
	}


/*static int cipher_des_ecb_init(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc)
{
	//des_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc);
	//des_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc);
	return 1;
}*/

static int cipher_des_ecb_code(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, unsigned int inl)
{
	printf("test");
	unsigned int i, bl; 
	bl = ctx->cipher->block_size;
	if(inl < bl) return 1;
	inl -= bl; 
	for(i=0; i <= inl; i+=bl) DES_ecb_encrypt((DES_cblock *)(in + i), (DES_cblock *)(out + i), ctx->cipher_data, ctx->encrypt);

	return 1; 
}

/*static int cipher_des_ecb_clean(EVP_CIPHER_CTX *ctx)
	{
		memset(data(ctx), 0, ctx->cipher->ctx_size);
		return 1;
	}*/
