#ifdef HAVE_IOSTREAM
#include <iostream>
#else
#include <iostream.h>
#endif

#include <stdio.h>

#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#include <strings.h>
#endif

#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>

#ifdef HAVE_IOSTREAM
#include <iostream>
#include <fstream>
#else
#include <iostream.h>
#include <fstream.h>
#endif

#ifdef HAVE_IOSTREAM
using namespace std;
#endif

// In memory rsa key generated by client
RSA * rsaKeyFromClient;

int ssltest1();
int ssltest2();
void rsa_status2 (int code, int arg, void * cb_arg);

// Default secret - used for symmetric encryption - these values may be superceeded by generated values
char       secret[]     = {2,8,4,9,3,7,3,2,6,1,9,9,7,9,7,0,6,6,0,4,9,4,5,6,2,3,9,6,2,1,4,8,3,0,0,7,1,0,9,1,0,7,2,4,5,4,4,4,7,6,5,3,3,7,7,9,7,2,7,1,3,3,9,8,6,8,4,1,6,0,2,3,4,2,4,0,1};
int        secretLen    = 77;

int main (int argc, char* argv[], char*[])
{
ssltest1();
ssltest2();

return(0);
}

// This routine reads in the rsa n and e components and creates the simulated server rsa key
// sent from client.
// The function then encrypts the secret key with the server rsa and then trys to
// decrypt with the client rsa.  On Windows 64-bit using VS 2005 with x64 mode, the
// decrypt fails inside of openssl function "BN_num_bits".
int ssltest2()
{
ERR_load_crypto_strings();

const char* clientNKeyFile = "C:\\ViewStorage\\allison_emv_test\\clientRsaKeyN.ref";
	 int c, kk=0;
	 FILE *input1 = fopen(clientNKeyFile,"r");
     char rsaKeyNFromClient[1000];
	 do {
      c = fgetc (input1);
	  if( c != EOF) rsaKeyNFromClient[kk] = (char) c;
	  kk++;
    } while (c != EOF);
    fclose (input1);

     const char* clientEKeyFile = "C:\\ViewStorage\\allison_emv_test\\clientRsaKeyE.ref";
	 FILE *input2 = fopen(clientEKeyFile,"r");
	 char rsaKeyEFromClient[1000];
	 kk = 0;
	 do {
      c = fgetc (input2);
	  if( c != EOF) rsaKeyEFromClient[kk] = (char) c;
	  kk++;
    } while (c != EOF);
    fclose (input2);

// Create the rsa key to simulate retrieving from client to server.
RSA * rsaKeyFromServer = RSA_new ();
BN_dec2bn (&rsaKeyFromServer->n, rsaKeyNFromClient);
BN_dec2bn (&rsaKeyFromServer->e, rsaKeyEFromClient);

cout << "client rsa n= " << BN_bn2dec (rsaKeyFromServer->n) << endl;
cout << "client rsa e= " << BN_bn2dec (rsaKeyFromServer->e) << endl;

int modulusSize = RSA_size(rsaKeyFromClient);

// Encrypt the secret key using the rsa key acquired from initial client to server.
// This step would normally be done on a server and the encrypted text be returned to
// the client.
unsigned char encryptedtext[1000];
int ciptotlen = RSA_public_encrypt(secretLen,
								  (const unsigned char *) secret,
														  encryptedtext,
								   rsaKeyFromServer, RSA_PKCS1_PADDING);	
if (ciptotlen == -1)
{
	int error = ERR_get_error();
}
		
// Now try and decrypt the above encrypted text using the original rsa key generated by the
// client on routine ssltest1().  Client would use this key since it would store it in memory
// This is currently failing for me on windows 64.  It fails inside of BN_num_bits.  This function returns
// 128 instead of 127 as with the windows 32 platform.  BN_BITS2 = 64 and i = 15 with the compile
// flags I am using.

/*
int BN_num_bits(const BIGNUM *a)
	{
	int i = a->top - 1;
	bn_check_top(a);

	if (BN_is_zero(a)) return 0;
	return ((i*BN_BITS2) + BN_num_bits_word(a->d[i]));
	}
*/

unsigned char decryptedtext[500];
int len1 = RSA_private_decrypt(modulusSize, (const unsigned char *)encryptedtext, decryptedtext, rsaKeyFromClient, RSA_PKCS1_PADDING);

if(len1 < 0)
{
	cerr << "len1 = " << len1 << endl;
    return 1;
}

// Return cleanly if decrypt works as planned.
return(0);
}

// This routine create a client rsa key and store in memory and also write out
// n and e so that we can simulate the server acquiring the n and e and creating
// its rsa key and encrypting the secret key.
int ssltest1()
{
	ERR_load_crypto_strings();

    // Make a populated RSA key pair for client
	char n[1000];
	char e[10];
	
	// create client rsa key
	rsaKeyFromClient = RSA_new ();
	rsaKeyFromClient = RSA_generate_key(1024, RSA_3, rsa_status2, NULL); 
	
	// Check the key
	if (RSA_check_key(rsaKeyFromClient) != 1)
	{
		ERR_print_errors_fp (stderr);
	}
    
	// verify n and e
	cout << "client rsa n= " << BN_bn2dec (rsaKeyFromClient->n) << endl;
    cout << "client rsa e= " << BN_bn2dec (rsaKeyFromClient->e) << endl;

	// Prepare to send RSA key values to the server file (output file)
	strcpy (n, BN_bn2dec (rsaKeyFromClient->n));
	strcpy (e, BN_bn2dec (rsaKeyFromClient->e));

	// create file for rsa n
	const char* clientNKeyFile = "C:\\ViewStorage\\allison_emv_test\\clientRsaKeyN.ref";
	 FILE *output1 = fopen(clientNKeyFile,"w+");
	 for (int j = 0; j < 1000; j++) {
      fputc(n[j],output1);
	 }
     fclose (output1);
	 
	 // create file for rsa e
     const char* clientEKeyFile = "C:\\ViewStorage\\allison_emv_test\\clientRsaKeyE.ref";
	 FILE *output2 = fopen(clientEKeyFile,"w+");
	 for (int k = 0; k < 10; k++) {
      fputc(e[k],output2);
	 }
     fclose (output2);
	
	 // return
	 return(0);
}
void rsa_status2 (int code, int arg, void * cb_arg)
{
// Used to print status during rsa gen generation
// do nothing at this time
}
