/**********************************************************************
 
	Copyright (C) 2003-2005
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomohito Nakajima <nakajima@zeta.co.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/

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

#include "ossl.h"
#include "openssl/rand.h"



int oSSL_RC4_decode(oSSL_data *decoded, oSSL_object * key, oSSL_data *org)
{
	oSSL_RC4_key *key_obj;

	if(key->type!=OSSL_RC4KEY){
		return -1;
	}
	
	key_obj = &key->obj.rc4_key;
	
	if(key_obj->use == OSSL_ENCODE_USE){
		return -2;
	}
	if(key->obj.rc4_key.use == 0){
		EVP_DecryptInit(&key_obj->ctx, EVP_rc4(), key_obj->data, 0);
		key_obj->use = OSSL_DECODE_USE;
	}
	
	oSSL_data_ensure_buff_size(decoded, org->len);
	
	if(!EVP_DecryptUpdate(&key_obj->ctx, decoded->data, &decoded->len, org->data, org->len)){
		d_f_ree(decoded);
		return -3;
	}
	
	return 0;
}

int oSSL_RC4_encode(oSSL_data *encoded, oSSL_object * key, oSSL_data * org)
{
	oSSL_RC4_key *key_obj;

	if(key->type!=OSSL_RC4KEY){
		return -1;
	}
	
	key_obj = &key->obj.rc4_key;
	
	if(key_obj->use == OSSL_DECODE_USE){
		return -2;
	}
	if(key->obj.rc4_key.use == 0){
		EVP_EncryptInit(&key_obj->ctx, EVP_rc4(), key_obj->data, 0);
		key_obj->use = OSSL_ENCODE_USE;
	}

	oSSL_data_ensure_buff_size(encoded, org->len);

	if(!EVP_EncryptUpdate(&key_obj->ctx, encoded->data, &encoded->len, org->data, org->len)){
		d_f_ree(encoded);
		return -3;
	}

	return 0;
	/* return oSSL_data_new(OSSL_RAW, encoded, encoded_len, TRUE); */
}

void oSSL_RC4_free(oSSL_object *obj){
	oSSL_RC4_key *key = &obj->obj.rc4_key;
	d_f_ree(key->data);
}

oSSL_data *oSSL_RC4_object2data(oSSL_object * obj/*, int convert_type*/)
{
	oSSL_data *ret;
	int key_len;
	if(obj->type != OSSL_RC4KEY){
		/* type missmatch */
		return NULL;
	}
	
	ret = oSSL_data_new(OSSL_RAW, 0, 0, TRUE, 0);
	oSSL_data_ensure_buff_size(ret, obj->obj.rc4_key.len);
	key_len = obj->obj.rc4_key.len;
	memcpy(ret->data, obj->obj.rc4_key.data, key_len);
	ret->len = obj->obj.rc4_key.len;
	ret->type = obj->type;
	
	return ret;
}

extern oSSL_method oSSL_RC4_key_method;
oSSL_object *oSSL_RC4_data2object(oSSL_data * data)
{
	oSSL_RC4_key *rc4key;
	oSSL_object *ret;
	
	if(data->type != OSSL_RC4KEY){
		/* type missmatch */
		return 0;
	}

	ret = oSSL_object_new(OSSL_RC4KEY);
	rc4key = &ret->obj.rc4_key;
	rc4key->data = d_alloc(data->len);
	rc4key->len = data->len;
	memcpy(rc4key->data, data->data, data->len);
	rc4key->use = 0;
	
	return ret;

}

oSSL_method oSSL_RC4_key_method = {
	OSSL_RC4KEY,
	oSSL_RC4_free,
	oSSL_RC4_object2data,
	oSSL_RC4_data2object,
	NULL,
	NULL
};

oSSL_object *oSSL_RC4_genkey(oSSL_data *key_data)
{
	oSSL_RC4_key *rc4key;
	EVP_MD_CTX ctx;
	oSSL_object *key_obj;
	
	key_obj = oSSL_object_new(OSSL_RC4KEY);
	rc4key = &key_obj->obj.rc4_key;
	rc4key->len = key_data->len;
	rc4key->use = 0;
	rc4key->data = d_alloc(EVP_MAX_MD_SIZE);
	
	EVP_DigestInit(&ctx, EVP_sha1());
	EVP_DigestUpdate(&ctx, key_data->data, key_data->len);
	EVP_DigestFinal(&ctx, rc4key->data, (unsigned int*)&rc4key->len);
	
	return key_obj;
}

void oSSL_RC4_init(){
	oSSL_types[OSSL_RC4KEY] = &oSSL_RC4_key_method;
}

