#include "ssl.h"
#include "talk.h"

BIO *bio_err = NULL;
BIO *sbio = NULL;
SSL_CTX *ctx = NULL;
RSA* rsakey = NULL;
SSL_METHOD *meth = NULL;
SSL *ssl = NULL;
X509* cert = NULL;

int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) {
	int err = X509_STORE_CTX_get_error(ctx);
	if (err == SELF_SIGNED_CERT) {
		/* Everything is fine, continue */
		return 1;
	}
	else {
		/* Anomalous error, exit */
		return 0;
	}
}

int generate_ss_cert() {

 	EVP_PKEY *pkey = NULL;
	
	/* generating a new RSAKEY */

	rsakey = RSA_generate_key(KEY_SIZE, EXPONENT_RSA, NULL, NULL);
	if (!rsakey) {
		return NO_RSA_KEY;
	}
  
	/* generating a new PRIVATE KEY */
  
	pkey = EVP_PKEY_new();	/* Creating key handler for new Certificate */
	
	if ((cert=X509_new()) == NULL) {
		RSA_free(rsakey);
		rsakey = NULL;
		return NO_X509;
	}

	if (!EVP_PKEY_assign_RSA(pkey, rsakey))
	{
		RSA_free(rsakey);
		X509_free(cert);
		rsakey = NULL;
		cert = NULL;
		/* Error adding RSA keys to certificate */
		return NO_RSA_TO_X509;
	}
	
	if (!X509_set_version(cert, 2)) { /* Version starts at 0 */ 
		RSA_free(rsakey);
		X509_free(cert);
		rsakey = NULL;
		cert = NULL;
		/* Error while versioning the Certificate */
		return NO_VERSION;
	}
  
	/* Set the Serial Number for the certificate */
	ASN1_INTEGER_set(X509_get_serialNumber(cert), SERIAL);
	
	X509_gmtime_adj(X509_get_notBefore(cert),0);
	X509_gmtime_adj(X509_get_notAfter(cert),(long)60*60*24*LAST_DAY);
	
	X509_set_pubkey(cert, pkey);
	
	
	/* Sign the certificate with our own key ("Self Sign") */
	if (!X509_sign(cert, pkey, EVP_md5()))
	{
		/* Could not self sign the Certificate */
		X509_free(cert);
		RSA_free(rsakey);
		cert = NULL;
		rsakey = NULL;
		return NO_SELF_SIGN;
	}

	return SUCCESS;
}

int init_client_ctx() {   

	/* Global system initialization, always return 1 */
	SSL_library_init();
	SSL_load_error_strings();
	
	/* An error write context */
	bio_err = BIO_new_fp(stderr,BIO_NOCLOSE);
	
	/* Generic method */
	meth = SSLv23_method();
	
	/* generate a new context */
	ctx = SSL_CTX_new(meth);
	if (!ctx)
		return FAIL;
	
	/* Set up a SIGPIPE handler */
    signal(SIGPIPE,sigpipe_handle);
    
    /* Load our keys and certificates*/
    SSL_CTX_use_certificate(ctx, cert);
		
    SSL_CTX_use_RSAPrivateKey(ctx, rsakey);

	return 0;
}

int show_peer_cert() {   
	X509 *cert;
    char *line;
	
	/* get the client certificate */
    cert = SSL_get_peer_certificate(ssl);
	if ( cert != NULL )
    {
        printf("Client certificates:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("Subject: %s\n", line);
        free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("Issuer: %s\n", line);
        free(line);
        X509_free(cert);
    }
    else
        printf("No certificates.\n");
	return SUCCESS;
}


int start_ssl() {
    if (generate_ss_cert() != SUCCESS)
	return FAIL;
	
	/* Build our SSL context*/
    if (init_client_ctx() != SUCCESS)
	return FAIL;
	
    if(ciphers)
	SSL_CTX_set_cipher_list(ctx,ciphers);
    	
    SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb);
    return SUCCESS;
}

int connect_ssl() {
    ssl = SSL_new(ctx);
    sbio = BIO_new_socket(sockt, BIO_NOCLOSE);
    SSL_set_bio(ssl,sbio,sbio);
    if(SSL_connect(ssl)<=0) {
    	printf("SSL connect error, continue in clear text...\n");
	return FAIL;
    }
    return SUCCESS;
}

int accept_ssl() {
	sbio=BIO_new_socket(sockt,BIO_NOCLOSE);
	ssl=SSL_new(ctx);
	SSL_set_bio(ssl,sbio,sbio);
	if((SSL_accept(ssl)<=0)) {
	    printf("SSL accept error, continue in clear text...\n");
	    return FAIL;
	}
	return SUCCESS;
}
