/*
 * Benchmark program for Advanced Crytography Engine
 *
 *  This program incorporates the C language implementation of the AES algorithm from Brian Gladman.
 *  
 *    //http://fp.gladman.plus.com/cryptography_technology/rijndael/
 *
 * 
 *  Centaur Technology is content to include and agree with his code use provisions
 *  with respect to this entire software package:
 *  
 *  "I am happy for this code to be used without payment provided that I don't carry
 *   any risks as a result.  I would appreciate an appropriate acknowledgement of the
 *   source of the code if you do use it in a product or activity provided to third
 *   parties. I would also be grateful for feedback on how the code is being used, any
 *   problems you encounter, any changes or additions that are desirable for particular
 *   processors and any more general improvements you would like to see (no promises mind!)."
 *   
 */

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


/*
 * This helps ensure that we get our data aligned correctly.  [ACE requires it's pointers to be 16-byte aligned.
 * [For further details, please read the VIA C5P Advanced Cryptography Engine Programmers Guide]
 *
 * Since we're gonna allocate a monster block anyway the extra space isn't noticed
 */

int dummy[1024] = {0};


#include "aes.h"


/*
 * Softare module key : ciphertext : plaintext (NIST Appendix 3 p35-38)
 */
int key1[4] = {0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c};
int res1[4] = {0xd8e0c469, 0x30047b6a, 0x80b7cdd8, 0x5ac5b470};
int txt1[4] = {0x33221100, 0x77665544, 0xbbaa9988, 0xffeeddcc}; 


/*
 * Key structures for Gladman's software AES implementation
 */
aes_encrypt_ctx ECTX;
aes_decrypt_ctx DCTX;

/*
 * The extended key based on key1 above. We compare this with the calculated key
 * to validate the software implementation
 */

int key0[44] = {0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c, \
                0xfd74aad6, 0xfa72afd2, 0xf178a6da, 0xfe76abd6, \
                0x0bcf92b6, 0xf1bd3d64, 0x00c59bbe, 0xfeb33068, \
                0x4e74ffb6, 0xbfc9c2d2, 0xbf0c596c, 0x41bf6904, \
                0xbcf7f747, 0x033e3595, 0xbc326cf9, 0xfd8d05fd, \
                0xe8a3aa3c, 0xeb9d9fa9, 0x57aff350, 0xaa22f6ad, \
                0x7d0f395e, 0x9692a6f7, 0xc13d55a7, 0x6b1fa30a, \
                0x1a70f914, 0x8ce25fe3, 0x4ddf0a44, 0x26c0a94e, \
                0x35874347, 0xb9651ca4, 0xf4ba16e0, 0xd27abfae, \
                0xd1329954, 0x685785f0, 0x9ced9310, 0x4e972cbe, \
                0x7f1d1113, 0x174a94e3, 0x8ba707f3, 0xc5302b4d};


/*
 * Various control words
 */

#define HWKEY		0x000
#define KEYSIZE_128	0x000 + 0x00a
#define ENCRYPT		0x000
#define DECRYPT		0x200
		
int HwkyEncrypt128[4] = {ENCRYPT + HWKEY + KEYSIZE_128, 0, 0, 0};
int HwkyDecrypt128[4] = {DECRYPT + HWKEY + KEYSIZE_128, 0, 0, 0};


int loopcounter = 0;
int testcounter = 0;
int buffersize = 0;
int HAS_ACE = 0;


/* 
 * Initial key for for timing tests.  Since we already checked for correct functionality, we
 * don't really care what we encrypt -- all bits look the same, performance-wise
 */

int ikey[8] = {0x5a5a5a5a};


/*
 *  Buffer for monte carlo mode key
 */

int dtxt[4]  = {0};

/*
 * Main encryption buffer.  Yes, we could malloc based on the specified buffer size, but then
 * we gotta worry about getting aligned correctly. So long as we don't initialize this buffer
 * there are no code size penalties
 */

int buffer_ecb[1024*1024*8];


#define XCRYPT_ECB  __asm _emit 0xF3 __asm _emit 0x0F __asm _emit 0xA7 __asm _emit 0xC8

#define XCRYPT_CBC	__asm _emit 0xF3 __asm _emit 0x0F __asm _emit 0xA7 __asm _emit 0xD0

#define NEW_KEYS	__asm pushfd __asm popfd

// __asm _emit 0xF3 = rep


void reset_buffers() {

	int i;

	for (i=0; i<4; i++) {
		dtxt[i] = 0;
		buffer_ecb[i] = 0;
	}
}


int ecb_encrypt_test() {

	int i;

	if (HAS_ACE) {
		__asm lea edx, HwkyEncrypt128
		__asm lea esi, buffer_ecb
		__asm lea edi, buffer_ecb
		__asm lea ebx, ikey
		__asm mov ecx, buffersize
		XCRYPT_ECB
	}
	else {

      		aes_encrypt_key128(key1, &ECTX);

		for (i=0; i<buffersize; i++) {
        		aes_encrypt(&buffer_ecb[i*4], &buffer_ecb[i*4], &ECTX);
		}
		
	}
}


int ecb_decrypt_test() {

	int i;

	if (HAS_ACE) {
		__asm lea edx, HwkyDecrypt128
		__asm lea esi, buffer_ecb
		__asm lea edi, buffer_ecb
		__asm lea ebx, ikey
		__asm mov ecx, buffersize
		XCRYPT_ECB
	}
	else {

      		aes_decrypt_key128(key1, &DCTX);

		for (i=0; i<buffersize; i++) {
        		aes_decrypt(&buffer_ecb[i*4], &buffer_ecb[i*4], &DCTX);
		}
		
	}
}





/*
 *  The expected results of the Monte Carlo test.  We don't do this massive
 *  test except with the ACE hardware because in software it will take too long
 */

unsigned int ecb_monte_e128[4] = {0xAB7743A0, 0xD0B059E2, 0x402DBAB5, 0x1B9701A5};
unsigned int ecb_monte_d128[4] = {0x378BBFF5, 0x1F2E6F13, 0x576FEC6B, 0xBAE32120};

int monte_carlo_ecb() {

	int j, k, n;

	reset_buffers();
	for (k=0; k < 400; k++) {

      __asm lea edx, HwkyEncrypt128
	  __asm lea ebx, dtxt

	  NEW_KEYS
	  for (n=0; n<10000; n++) {
	    __asm lea esi, buffer_ecb
	    __asm lea edi, buffer_ecb
	    __asm mov ecx, 1
		XCRYPT_ECB
	  }

	  
	  for (j=0; j<4; j++) {
	    dtxt[j] = dtxt[j] ^ buffer_ecb[j];	  
	  }
	}

	for (j=0; j<4; j++) {
	  if (buffer_ecb[j] != ecb_monte_e128[j]) {
	  printf("ECB 128BIT ENCRYPT MONTE CARLO FAILS\n");
	  exit(0);
	  }
	}

	reset_buffers();
	for (k=0; k < 400; k++) {

      __asm lea edx, HwkyDecrypt128
	  __asm lea ebx, dtxt

	  NEW_KEYS
	  for (n=0; n<10000; n++) {
	    __asm lea esi, buffer_ecb
	    __asm lea edi, buffer_ecb
	    __asm mov ecx, 1
		XCRYPT_ECB
	  }

	  for (j=0; j<4; j++) {
	    dtxt[j] = dtxt[j] ^ buffer_ecb[j];	  
	  }
	}

	for (j=0; j<4; j++) {
	  if (buffer_ecb[j] != ecb_monte_d128[j]) {
	    printf("ECB 128BIT DECRYPT MONTE CARLO FAILS\n");
	    exit(0);
	  }
	}
}


void process_command_line(int argc, char **argv)
{

        int i;
        char * c;

        for (i = 1; i < argc; i++)  {

                c = argv[i];

                if (!strcmp(c,"-soft"))
			HAS_ACE = 0;

                if (!strcmp(c,"-rep"))
                        loopcounter = atoi(argv[++i]);
                        
                if (!strcmp(c,"-kb"))
                        buffersize = 64 * atoi(argv[++i]);


	}

	// Default to 8K
	if (buffersize == 0 )
		buffersize = 512;


	// And don't let it get bigger than 32M
	if (buffersize > 1024*32*64)
		buffersize = 1024*32*64;

}





int main(int argc, char **argv) {

	clock_t start;
	unsigned int j;
	float elapsed, datarate;
	
	SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
  



/*
 *  Detect whether or not we're running on a C5P with the ACE unit active
 */

	__asm mov eax, 0xC0000000
	__asm cpuid

	__asm cmp eax, 0xC0000001
	__asm jnz  local_label

	__asm cpuid

	__asm and edx, 0xC0
	__asm jz  local_label

	__asm mov HAS_ACE, 1 

    __asm local_label:

	printf("\n");


/*
 *  Check the command line to possibly tweak the operation
 */
        process_command_line(argc, argv);



	if (HAS_ACE) {

	  printf("Detected VIA C3 Nehemiah processor (stepping 8) with Advanced Cryptography Engine.\n\n");
      	  printf("Validating ACE hardware with 128-bit ECB Monte Carlo Test.\n");


/* If this test fails, it will print an error and exit */
	  monte_carlo_ecb();
	  printf("Monte Carlo test PASSED\n\n");

/* Set the hardware default if the user hasn't specified a loop count */
	  if (loopcounter == 0)
	    loopcounter = 1000000;
	  NEW_KEYS

	}
	else {

	  printf("Validating software AES module (Brian Gladman's implementation) with NIST test case.\n");
		
	  aes_encrypt_key128(key1, &ECTX);
	  aes_decrypt_key128(key1, &DCTX);
  	  
	  for (j=0; j<44; j++) {
		
		if (key0[j] != (int) ECTX.ks[j] ) {
			printf("SOFTWARE ECB KEY GENERATION FAILED\n");
		    	exit(0);
		}
		
	  }		


	  aes_encrypt(txt1, txt1, &ECTX);

	  for (j=0; j<4; j++) {

		if (txt1[j] != res1[j]) {
			printf("SOFTWARE ECB TEST FAILED\n");
		    	exit(0);
		}

	  aes_encrypt(dtxt, dtxt, &ECTX);
	  aes_decrypt(dtxt, dtxt, &DCTX);

	  for (j=0; j<4; j++) {
		
		if (dtxt[j]) {
			printf("SOFTWARE ECB TEST FAILED\n");
		    	exit(0);
		}
	  }


	
	  }
	  
	  printf("Software module PASSED\n\n");

	  if (loopcounter == 0)
	    loopcounter = 10000;
	   
	}

		
/* Do the test once to fill the cache before starting the clock */
		
	printf("Encrypting %dKB block %d times.\n", buffersize/64, loopcounter);

	ecb_encrypt_test();
	start = clock();


	for (j=0; j < loopcounter; j++) {
		ecb_encrypt_test();
	}

	elapsed = ((float) (clock() - start)) / ( (float) CLOCKS_PER_SEC);
	datarate = (float) j * buffersize * 128. / elapsed;
	printf("ECB encryption rate is %.0f million bits/sec\n\n", (datarate / 1000000.));



	printf("Decrypting %dKB block %d times.\n", buffersize/64, loopcounter);

	ecb_decrypt_test();
	start = clock();


	for (j=0; j < loopcounter; j++) {
		ecb_decrypt_test();
	}

	elapsed = ((float) (clock() - start)) / ( (float) CLOCKS_PER_SEC);
	datarate = (float) j * buffersize * 128. / elapsed;
	printf("ECB decryption rate is %.0f million bits/sec\n\n", (datarate / 1000000.));

	
	exit(0);

}



	