/*
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
*/

#include "ossl.h"
#include "openssl/rand.h"
#include <string.h>

#include "init.h"
#include "utils.h"
#include "machine/task.h"

int gbrsa_test_main()
{
#define KEY_BIT_LENGTH 1024

	/* open ssl library test code (without reference to GBS) */
	unsigned char test_plain_text[] = "aiueo kakikukeko sasisuseso tachituteto aaaaaaaaa b";
	unsigned char decrypted_buff[256];
	int crypted_data_size;
	RSA *rsa;
	int encodable_length;
	unsigned char *crypted;
	int crypted_len;
	int decrypted_len;
	
	encodable_length = 64-11;
	
	rsa = RSA_generate_key(KEY_BIT_LENGTH, RSA_F4, NULL, NULL);
	
	crypted_data_size = RSA_size(rsa);
	crypted = (unsigned char*)malloc(crypted_data_size);

	crypted_len = RSA_private_encrypt(
			sizeof(test_plain_text), 
			(unsigned char*)test_plain_text, 
			crypted,
			rsa,
			RSA_PKCS1_PADDING);
	
	decrypted_len = RSA_public_decrypt(
		crypted_len,
		crypted,
		decrypted_buff,
		rsa,
		RSA_PKCS1_PADDING);
	
	if(memcmp(test_plain_text, decrypted_buff, sizeof(test_plain_text)) == 0){
		printf("RSA openssl lib ok\n");
	}
	else{
		printf("RSA openssl lib err\n");
		exit(-1);
	}

	RSA_free(rsa);

	
	/* gbs RSA encode/decode test code */
	{
		oSSL_object *public_key;
		oSSL_object *private_key;
		oSSL_data *encoded;
		oSSL_data *org;
		oSSL_data *decoded;
		
		oSSL_data *private_key_data;
		oSSL_object *private_key_loaded;

		if(oSSL_RSA_genkey(&public_key, &private_key, KEY_BIT_LENGTH) == -1){
			printf("err 1");
		}
		
		private_key_data = oSSL_object2data(private_key);
		private_key_loaded = oSSL_data2object(private_key_data);
		
		org = oSSL_data_new(OSSL_RAW, (unsigned char*)"hogehogefoobar abcdefghijklmn", strlen("hogehogefoobar abcdefghijklmn")+1, FALSE, 0);
		encoded = oSSL_RSA_encode(public_key, org);
		if(encoded == 0){
			printf("err 2");
		}
		
		oSSL_object_free(public_key);
		
		decoded = oSSL_RSA_decode(private_key_loaded, encoded);
		if(decoded == 0){
			printf("err 3");
		}
		
		oSSL_object_free(private_key);
		oSSL_data_free(encoded);
		
		if(memcmp(decoded->data, org->data, org->len) == 0){
			printf("gbrsa ok\n");
		}
		else{
			printf("gbrsa error\n");
		}
		printf("test decoded data = %s\n", decoded->data);

		oSSL_data_free(decoded);
		oSSL_data_free(org);
	}
	return 0;
}

int gbrc4_test_main()
{
	int result;
	oSSL_object *encode_key;
	oSSL_data *encode_key_data;
	oSSL_object *decode_key;
	oSSL_data key_data;
	
	{
		/* generate random key */
		key_data.len = 128;
		key_data.data = d_alloc(key_data.len);
		RAND_bytes(key_data.data, key_data.len);
		
		/* make key object */
		encode_key = oSSL_RC4_genkey(&key_data);
		if(encode_key==0){
			printf("error oSSL_RC4_genkey 1\n");
			exit(-1);
		}
		
		encode_key_data = oSSL_object2data(encode_key);
		oSSL_object_free(encode_key);
		encode_key = oSSL_data2object(encode_key_data);
		
		decode_key = oSSL_RC4_genkey(&key_data);
		if(decode_key==0){
			printf("error oSSL_RC4_genkey 2\n");
			exit(-1);
		}
		d_f_ree(key_data.data);
	}
	
	{
		oSSL_data *encoded_data;
		oSSL_data *decoded_data;
		oSSL_data *org_data;
		org_data = oSSL_data_new(OSSL_RAW, (unsigned char*)"test abcdefg", strlen("test abcdefg")+1, FALSE, 0);
		encoded_data = oSSL_data_new(OSSL_RAW, NULL, 0, TRUE, 0);

		/* encode */
		result = oSSL_RC4_encode(encoded_data, encode_key, org_data);
		oSSL_object_free(encode_key);
		if(result < 0){
			printf("error oSSL_RC4_encode \n");
			exit(-1);
		}
		
		decoded_data = oSSL_data_new(OSSL_RAW, NULL, 0, TRUE, 0);
		/* decode */
		result = oSSL_RC4_decode(decoded_data, decode_key, encoded_data);
		oSSL_object_free(decode_key);
		oSSL_data_free(encoded_data);
		if(result < 0){
			printf("error oSSL_RC4_decode \n");
			exit(-1);
		}
		
		if(memcmp(org_data->data, decoded_data->data, org_data->len) == 0){
			printf("gbrc4 ok\n");
		}
		else{
			printf("gbrc4 error\n");
		}
		
		oSSL_data_free(decoded_data);
		oSSL_data_free(org_data);
	}

	return 0;
}

void init_gb(){
	init_task(INI_DONTWAITCHI);
	init_tick();
	init_d_alloc();
	init_longchar();
	init_stream();
	init_utils();
	init_resolve();
	init_netutils();
	init_set_buffer();
}



/****************************/
/* SSL connection test */
/****************************/
#include "server.h"
#include "client.h"
#include "pri_level.h"

#define TEST_PORT (12346)

#define TEST_SERVER_HOST_NAME "ossl.localtest.org"
#define TEST_SERVER_CERT_FILE "server_cert.pem"
#define TEST_SERVER_PRIVATEKEY_FILE "server_privatekey.pem"
#define TEST_SERVER_PRIVATEKEY_PASSWORD "test_password"

#define TEST_CA_CERT_FILE "ca_cert.pem"
#define TEST_CA_PRIVATEKEY_FILE "ca_privatekey.pem"
#define TEST_CA_PRIVATEKEY_PASSWORD "test_ca_password"

#define TEST_CLIENT_CERT_FILE "client_cert.pem"
#define TEST_CLIENT_PRIVATEKEY_FILE "client_privatekey.pem"
#define TEST_CLIENT_PRIVATEKEY_PASSWORD "test_client_password"

#define TEST_CLIENT_TRUST_CA_CERT_FILE "ca_cert.pem"


oSSL_context *ssl_test_get_server_context()
{
	oSSL_context *ret;
	oSSL_object *cert;
	oSSL_object *private_key;
	
	/* load server's certificate */
	cert = oSSL_object_new(OSSL_CERTIFICATE);
	if(!oSSL_object_load_pem(cert, TEST_SERVER_CERT_FILE, NULL)){
		printf("error oSSL_object_load_pem TEST_SERVER_CERT_FILE\n");
	}
	
	/* load server's private key */
	private_key = oSSL_object_new(OSSL_RSA_PRIVATEKEY);
	if(!oSSL_object_load_pem(private_key, TEST_SERVER_PRIVATEKEY_FILE, TEST_SERVER_PRIVATEKEY_PASSWORD)){
		printf("error oSSL_object_load_pem TEST_SERVER_PRIVATEKEY_FILE\n");
	}
	
	ret = oSSL_context_new(cert, private_key, 0);
	
	oSSL_object_free(cert);
	oSSL_object_free(private_key);


	cert = oSSL_object_new(OSSL_CERTIFICATE);
	if(!oSSL_object_load_pem(cert, TEST_CA_CERT_FILE, NULL)){
		printf("err oSSL_object_load_pem\n");
	}
	
	if(!oSSL_add_root_certificate(ret, cert)){
		printf("err oSSL_add_root_certificate\n");
	}

	oSSL_object_free(cert);
	
	return ret;
}

void ssl_test_server_thread()
{
	STREAM *st;
	int port;
	ACCESS_KEY *key;
	int read_size;
	oSSL_context *ctx;
	
	port = TEST_PORT;
	st = init_server(&port, port, 1);
	
	ctx = ssl_test_get_server_context();
	
	do{
		STREAM *client_st;
		STREAM *ossl_client_st;
		char buf[256];
		
		key = accept_connection(st);
		client_st = key->s;
		
		/* open ssl connection over socket STREAM */ 
		ossl_client_st = s_open_oSSL(
				client_st, 
				ctx, 
				OSSL_FLAG_ACCEPT|OSSL_FLAG_VERIFY,  
				l_string(std_cm, "ossl test client") );
		memset(buf, 0, sizeof(buf));
		read_size = s_read(ossl_client_st, buf, sizeof(buf));
		printf("%s", buf);
		
		s_close(ossl_client_st);
	}while(1);
	oSSL_context_free(ctx);
}


oSSL_context *ssl_test_get_client_context()
{
	oSSL_context *ret;
	oSSL_object *cert;

	oSSL_object *private_key;
	
	cert = oSSL_object_new(OSSL_CERTIFICATE);
	if(!oSSL_object_load_pem(cert, TEST_CLIENT_CERT_FILE, NULL)){
		printf("error oSSL_object_load_pem TEST_CLIENT_CERT_FILE\n");
	}
	
	private_key = oSSL_object_new(OSSL_RSA_PRIVATEKEY);
	if(!oSSL_object_load_pem(private_key, TEST_CLIENT_PRIVATEKEY_FILE, TEST_CLIENT_PRIVATEKEY_PASSWORD)){
		printf("error oSSL_object_load_pem TEST_CLIENT_PRIVATEKEY_FILE\n");
	}
	
	ret = oSSL_context_new(cert, private_key, 0);
	
	oSSL_object_free(cert);
	oSSL_object_free(private_key);
	
	cert = oSSL_object_new(OSSL_CERTIFICATE);
	if(!oSSL_object_load_pem(cert, TEST_CLIENT_TRUST_CA_CERT_FILE, NULL)){
		printf("err oSSL_object_load_pem\n");
	}
	
	if(!oSSL_add_root_certificate(ret, cert)){
		printf("err oSSL_add_root_certificate\n");
	}
	
	return ret;
}

void ssl_test_client_thread()
{
	int err;
	STREAM *st;
	STREAM *ossl_st;
	int write_size;
	int write_total;
	oSSL_context *ctx;
	char *msg = "This is the message client to server.\n";
	
	write_total = 0;
	ctx = ssl_test_get_client_context();
	
	do{
		sleep(1);
		st = new_connection(
			&err,
			"localhost",
			0,
			TEST_PORT,
			NULL,
			NULL);
	}while(!st);
	
	ossl_st = s_open_oSSL(
			st, 
			ctx, 
			OSSL_FLAG_VERIFY|OSSL_FLAG_CONNECT,
			l_string(std_cm, TEST_SERVER_HOST_NAME) /* host name for check peer certificate */
			);
	
	do{
		write_size = s_write(ossl_st, msg+write_total, strlen(msg+write_total));
		if(write_size == -1){
			return;
		}
		write_total += write_size;
	}while( write_total < (int)strlen(msg) );
	
	oSSL_context_free(ctx);
	sleep(2);
}

void ssl_connect_test()
{
	create_task(ssl_test_server_thread, 0, PRI_NETWORK);
	ssl_test_client_thread();
}

/********************/
/* test CA */
/********************/

void test_create_cert_files()
{
	oSSL_cert_extension server_cert_extension;
	
	oSSL_cert_extension ca_cert_extensions[2];
	
	oSSL_object *ca_public_key,
				*ca_private_key,
				*ca_csr,
				*ca_cert,
				*server_public_key,
				*server_private_key,
				*server_csr,
				*server_sert,
				*client_public_key,
				*client_private_key,
				*client_csr,
				*client_sert;
	
	L_CHAR *ext_text_data;
	
		
	oSSL_RSA_genkey(&ca_public_key, &ca_private_key, 1024);
	
	/* gbs custom extension comment. the field name SN_gb_comment is already registerd in oSSL_init() */
	ca_cert_extensions[0].name = l_string(std_cm, SN_gb_comment);
	ca_cert_extensions[0].value = l_string(std_cm, "cmt:extTestValue");
	
	/* subjectAltName */
	ca_cert_extensions[1].name = l_string(std_cm, "subjectAltName");
	ca_cert_extensions[1].value = l_string(std_cm, "DNS:" TEST_SERVER_HOST_NAME);

	ca_csr = oSSL_create_csr(
		l_string(std_cm, "JP"),
		l_string(std_cm, "Tokyo"),
		l_string(std_cm, "Meguro-ku"),
		l_string(std_cm, "Zeta"),
		l_string(std_cm, "Development"),
		l_string(std_cm, "gbs test CA"),
		l_string(std_cm, "test@globalbase.org"),
		ca_private_key,
		ca_cert_extensions,
		2);
	
	/* create self sign certificate for test CA */
	ca_cert = oSSL_create_certificate(ca_csr, ca_private_key, NULL, 1, 60*60*24*365, OSSL_KEY_USAGE_CA);
	
	/* check gb custom extension data is OK? */
	ext_text_data = oSSL_get_ext_cert_data(l_string(std_cm, SN_gb_comment), ca_cert);
	printf("ext_test_data=%s\n", n_string(std_cm, ext_text_data));
	
	/* write CA certificate */
	oSSL_object_save_pem(ca_cert, TEST_CA_CERT_FILE, NULL);
	
	/* write CA privatekey */
	oSSL_object_save_pem(ca_private_key, TEST_CA_PRIVATEKEY_FILE, TEST_CA_PRIVATEKEY_PASSWORD);
	
	/* create test server key */
	oSSL_RSA_genkey(&server_public_key, &server_private_key, 1024);
	
	server_cert_extension.name = l_string(std_cm, "subjectAltName");
	server_cert_extension.value = l_string(std_cm, "DNS:" TEST_SERVER_HOST_NAME);
	server_csr = oSSL_create_csr(
		l_string(std_cm, "JP"),
		l_string(std_cm, "Tokyo"),
		l_string(std_cm, "Meguro-ku"),
		l_string(std_cm, "Zeta"),
		l_string(std_cm, "Development"),
		l_string(std_cm, TEST_SERVER_HOST_NAME),
		l_string(std_cm, "server-test@globalbase.org"),
		server_private_key,
		&server_cert_extension,
		1);
	
	server_sert = oSSL_create_certificate(server_csr, ca_private_key, ca_cert, 2, 60*60*24*365, OSSL_KEY_USAGE_SERVER);
	
	oSSL_object_save_pem(server_sert, TEST_SERVER_CERT_FILE , NULL);
	oSSL_object_save_pem(server_private_key, TEST_SERVER_PRIVATEKEY_FILE, TEST_SERVER_PRIVATEKEY_PASSWORD);
	
	
	/* create test client key */
	oSSL_RSA_genkey(&client_public_key, &client_private_key, 1024);
	client_csr = oSSL_create_csr(
		l_string(std_cm, "JP"),
		l_string(std_cm, "Tokyo"),
		l_string(std_cm, "Meguro-ku"),
		l_string(std_cm, "Zeta"),
		l_string(std_cm, "Development"),
		l_string(std_cm, "ossl test client"),
		l_string(std_cm, "clinet-test@globalbase.org"),
		client_private_key,
		NULL,
		0);
	client_sert = oSSL_create_certificate(client_csr, ca_private_key, ca_cert, 3, 60*60*24*365, OSSL_KEY_USAGE_CLIENT);

	oSSL_object_save_pem(client_sert, TEST_CLIENT_CERT_FILE, NULL);
	oSSL_object_save_pem(client_private_key, TEST_CLIENT_PRIVATEKEY_FILE, TEST_CLIENT_PRIVATEKEY_PASSWORD);

	oSSL_object_free(ca_public_key);
	oSSL_object_free(ca_private_key);
	oSSL_object_free(ca_csr);
	oSSL_object_free(ca_cert);
	
	oSSL_object_free(server_public_key);
	oSSL_object_free(server_private_key);
	oSSL_object_free(server_csr);
	oSSL_object_free(server_sert);
	
	oSSL_object_free(client_public_key);
	oSSL_object_free(client_private_key);
	oSSL_object_free(client_csr);
	oSSL_object_free(client_sert);

}

int main(int argc, char* argv[])
{
	/*
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
*/
	init_gb();
	oSSL_init();
	
	gbrsa_test_main();
	gbrc4_test_main();

	test_create_cert_files();
	
	ssl_connect_test();
	
	oSSL_close();
	return 0;
}
