/*
[]======================================================================[]
	pkcsdemo.cpp
	Copyright (C) 2002 Feitian Technologies Co., Ltd. All rights reserved.

	Comment :
		Demonstrates the use of the PKCS#11 APIs to interface with the
		ePass1000 and perform cryptographic operations.

	Note :
		This sample program requires generation of an RSA public/private
		key pair on the ePass1000. Before running this program, please
		make sure that a properly initialized ePass1000 is available and
		plugged into the USB port on your system.

	Warning :
		Please DO NOT remove the ePass1000 from your system while running
		this demo program. This program writes keys to the ePass1000. If
		the ePass1000 is removed while the library is writing its key data,
		the data may become corrupted. Once data becomes corrupted, you may
		need to reinitialize the token before it can be used by a PKCS#11-
		based application.

	History :
	[2002/04/10 Water Bird] Created.
[]======================================================================[]
*/

#include "stdio.h"
#include "conio.h"
#include "malloc.h"
#include "string.h"

#include <cryptoki.h>   //files cryptoki.h, pkcs11.h, pkcs11t.h and pkcs11f.h must be in ../vc/include


#define	countof(a)			(sizeof(a)/ sizeof(CK_ATTRIBUTE))

const unsigned long MAX_PIN_LEN	= 8;	// Max length of user PIN.
const CK_ULONG MODULUS_BIT_LENGTH = 1024;	// size of RSA key in bits

#define START_OP(szMsg)												\
{																	\
	printf ("\nOP: %s", szMsg);									\
}

#define CHECK_OP(rvRet)												\
{																	\
	if(CKR_OK == (rvRet))											\
	{																\
		printf ("....[OK].");										\
	}																\
	else															\
	{																\
		printf ("....[FAILED]\n\t\t\tErrCode = 0x%08X.", (rvRet));	\
		break;														\
	}																\
}

#define SHOW_ERROR(szMsg)											\
{																	\
	printf ("\n\nSTOP! %s!", (szMsg));								\
	break;															\
}

#define SHOW_INFO(szMsg)											\
{																	\
	printf ("%s", (szMsg));											\
}

void RunRsaKeyGenerationTest (CK_SESSION_HANDLE hSession);
void ShowTitle(void);

//////////////////////////////////////////////////////////////////////////
// main process
//////////////////////////////////////////////////////////////////////////
void main(int argc, char* argv[])
{
	CK_RV				rv;
	CK_ULONG			ulCount		 = 0;		
	CK_SLOT_ID_PTR		pSlotList	 = NULL_PTR;
	CK_VOID_PTR			pApplication = "My Application";
	CK_SESSION_HANDLE	hSession	 = 0;

	ShowTitle();

	do{
		//  Initialize the ePass1000 PKCS#11 library.
		START_OP("Initialize the ePass1000 PKCS#11 library.")
		rv = C_Initialize(NULL_PTR);
		CHECK_OP(rv)

		// Alternatively, you may use the C_GetFunctionList function to obtain
		// a pointer to the list of PKCS#11 function pointers. This function
		// is used to load functions of a shared PKCS#11 library. After
		// function pointers are loaded, you may access the C_Initialize
		// function from the list to initialize the library.

		// Get the number of slots with a token attached.
		START_OP("Get the slots information.")
		rv = C_GetSlotList(TRUE, NULL_PTR, &ulCount);
		CHECK_OP(rv)
		if(ulCount <= 0)
			SHOW_ERROR("No slot with token attached.");

		// Allocate memory buffer for the slot list.
		START_OP("Allocate memory for slots.")
		pSlotList = (CK_SLOT_ID_PTR)malloc(ulCount * sizeof(CK_SLOT_ID));
		if (! pSlotList) 
			CHECK_OP(CKR_HOST_MEMORY)
		else
			CHECK_OP(CKR_OK)

		// Okay, we have the buffer allocated. Let's get the slot list.
		START_OP("Get the slots information.")
		rv = C_GetSlotList(TRUE, pSlotList, &ulCount);
		CHECK_OP(rv)

		// Open a session to communicate with the token.
		START_OP("Open a session to communicate with the ePass1000.")
		rv = C_OpenSession(pSlotList[0],  CKF_RW_SESSION | CKF_SERIAL_SESSION,
						   &pApplication, NULL_PTR, &hSession);
		CHECK_OP(rv)
		
		// With a session opened, you may now invoke cryptographic functions,	
		// such as key generation, encryption, decryption, and so on.			

		SHOW_INFO("\n\nThe next demo will generate a RSA key pair on ePass1000.")
		RunRsaKeyGenerationTest(hSession);

		// Release the session handle after use. 
		START_OP("Release the session handle.");
		C_CloseSession(hSession);
		CHECK_OP(CKR_OK)
	}while (0);

	// Free any allocated memory prior to exit.
	if (pSlotList)
		free(pSlotList);

	// Don't forget to invoke the C_Finalize function before exiting your routine.
	START_OP("Finalize the library.")
	C_Finalize(NULL_PTR);
	SHOW_INFO("....[OK]")

	printf("\n\nProgram terminated.\n\nPress any key to exit...\n\n");
	getch();
}


void ShowTitle(void)
{
	printf("\n\n");
	printf("[]==================================================[]\n");
	printf(" |             ePass1000 PKCS#11 Demo               |\n");				
	printf("[]==================================================[]\n");
}

//////////////////////////////////////////////////////////////////////////
//	RunRsaKeyGenerationTest()
//
//	Provide example on how to generate a public/private key pair, such as 
//	an RSA key, and use the key pair to sign and verify messages.
//
//	The objects created are "token" objects that reside in the token. 
//////////////////////////////////////////////////////////////////////////

void RunRsaKeyGenerationTest (CK_SESSION_HANDLE	hSession)
{
	CK_RV rv;
	CK_BBOOL bTrue = TRUE;
	CK_BYTE bPIN[MAX_PIN_LEN + 1]; // 4 bytes <= PIN <= 8 bytes
	CK_ULONG ulModulusBits = MODULUS_BIT_LENGTH; 
	CK_BYTE subject[] = "Sample RSA Key Pair";
	CK_ULONG keyType = CKK_RSA;

	CK_OBJECT_HANDLE hPubKey = 0;
	CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
	CK_ATTRIBUTE pubTemplate[] = { 
		{CKA_CLASS,			&pubClass,		sizeof(pubClass)},
		{CKA_KEY_TYPE,		&keyType,		sizeof(keyType)},
		{CKA_SUBJECT,		subject,		sizeof(subject)},
		{CKA_MODULUS_BITS,	&ulModulusBits, sizeof(ulModulusBits)},
		{CKA_ENCRYPT,		&bTrue,			sizeof(bTrue)},
		{CKA_TOKEN,			&bTrue,			sizeof(bTrue)},
		{CKA_WRAP,			&bTrue,			sizeof(bTrue)},
	};	// Without specifying CKA_PRIVATE attribute in this case, 
		// a public key will be created by default. 

	CK_OBJECT_HANDLE hPriKey		= 0;
	CK_OBJECT_CLASS priClass	= CKO_PRIVATE_KEY;
	CK_ATTRIBUTE priTemplate[] = {
		{CKA_CLASS,			&priClass,	sizeof(priClass)},
		{CKA_KEY_TYPE,		&keyType,	sizeof(keyType)},
		{CKA_SUBJECT,		subject,	sizeof(subject)},
		{CKA_DECRYPT,		&bTrue,		sizeof(bTrue)},
		{CKA_PRIVATE,		&bTrue,		sizeof(bTrue)},
		{CKA_SENSITIVE,		&bTrue,		sizeof(bTrue)},
		{CKA_TOKEN,			&bTrue,		sizeof(bTrue)},
		{CKA_EXTRACTABLE,	&bTrue,		sizeof(bTrue)},
		{CKA_UNWRAP,		&bTrue,		sizeof(bTrue)},
	};

    CK_MECHANISM keyGenMechanism = {
		CKM_RSA_PKCS_KEY_PAIR_GEN, 
		NULL_PTR, 
		0
	};	
	CK_MECHANISM ckMechanism = {CKM_RSA_PKCS, NULL_PTR, 0};
	CK_BYTE pbMsg[] = "ePass1000 bring you into e-Business time.";
	CK_ULONG ulMsgLen = strlen((const char *)pbMsg);
	CK_BYTE bSignatureBuffer[MODULUS_BIT_LENGTH] = {0};
	CK_ULONG ulSignatureLen = 0;
	CK_BYTE_PTR pbCipherBuffer = NULL;
	CK_ULONG ulCipherLen = 0;
	CK_BYTE_PTR pbRestoredMsg = NULL;
	CK_ULONG ulRestoredMsgLen = 0;

	SHOW_INFO("\nYou will need to enter your USER-PIN to log in.")

	do{
		// When the CKA_TOKEN attribute is set to TRUE, the key objects are	
		// to be created on the token. In this case, user logon is required
		// before a "token" object can be stored in the hardware token.
		memset(bPIN, 0, MAX_PIN_LEN + 1); 
		SHOW_INFO("\n\nPlease enter your USER-PIN\n")
		scanf ("%s", bPIN);

		START_OP("Try to login as user with USER-PIN.")
		rv = C_Login(hSession, CKU_USER, bPIN, (CK_ULONG)strlen((char *)bPIN));
		CHECK_OP(rv)

	}while (rv != CKR_OK);

	if(rv == CKR_OK)
	{
		do {
			// Generate a 1024-bit RSA key pair on the token.
			START_OP("Generating public/private key pair.");
			rv = C_GenerateKeyPair(hSession, &keyGenMechanism,
								   pubTemplate, countof(pubTemplate),
								   priTemplate, countof(priTemplate),
								   &hPubKey, &hPriKey);
			CHECK_OP(rv)

			// Try Sign and Verify operations with the key pair.

			// Sign a message. 
			SHOW_INFO("\nThe message to be signed is \"")
			SHOW_INFO(pbMsg)
			SHOW_INFO("\".");

			ulSignatureLen	= sizeof(bSignatureBuffer);

			START_OP("Signing initialize.");
			rv = C_SignInit(hSession, &ckMechanism, hPriKey);
			CHECK_OP(rv);

			START_OP("Sign the message.")
			rv = C_Sign(hSession, 
				pbMsg,
				ulMsgLen, 
				bSignatureBuffer, &ulSignatureLen);
			CHECK_OP(rv)

			// Verify the previously signed message.
			START_OP("Verifying initialize.")
			rv = C_VerifyInit(hSession, &ckMechanism, hPubKey);
			CHECK_OP(rv)
	
			START_OP("Verify the message.")
			rv = C_Verify(hSession, 
				pbMsg, ulMsgLen, 
				bSignatureBuffer, ulSignatureLen);
			CHECK_OP(rv)

			// Encrypt a message. 
			SHOW_INFO("\nThe message to be Encrypt is \"")
			SHOW_INFO(pbMsg)
			SHOW_INFO("\".");

			START_OP("Encrypting initialize.")
			rv = C_EncryptInit(hSession,
				&ckMechanism,
				hPubKey);
			CHECK_OP(rv)

			START_OP("Obtain the size of the encrypted message.")
			rv = C_Encrypt(hSession, pbMsg, ulMsgLen, NULL_PTR, &ulCipherLen);
			CHECK_OP(rv)

			START_OP("Allocate buffer for the encrypted message.")
			pbCipherBuffer = (CK_BYTE_PTR)malloc(ulCipherLen);
			if (! pbCipherBuffer)
				CHECK_OP(CKR_HOST_MEMORY)
			else
				CHECK_OP(CKR_OK);

			START_OP("Encrypt the message.");
			rv = C_Encrypt(hSession, pbMsg, ulMsgLen, pbCipherBuffer, &ulCipherLen);
			CHECK_OP(rv);

			START_OP("Decrypting initialize.")
			rv = C_DecryptInit(hSession,
				&ckMechanism,
				hPriKey);
			CHECK_OP(rv)

			START_OP("Obtain the size of the decrypted message.")
			rv = C_Decrypt(hSession, pbCipherBuffer, ulCipherLen, NULL_PTR, &ulRestoredMsgLen);
			CHECK_OP(rv)

			START_OP("Allocate buffer for the decrypted message.")
			pbRestoredMsg = (CK_BYTE_PTR)malloc(ulRestoredMsgLen + 1);
			if (! pbRestoredMsg)
				CHECK_OP(CKR_HOST_MEMORY)
			else
				CHECK_OP(CKR_OK);
			memset(pbRestoredMsg, 0, ulRestoredMsgLen + 1);

			START_OP("Decrypt the message.")
			rv = C_Decrypt(hSession, pbCipherBuffer, ulCipherLen, pbRestoredMsg, &ulRestoredMsgLen);
			CHECK_OP(rv)

			// Decrypt a message. 
			SHOW_INFO("\nThe message decrypted is \"")
			SHOW_INFO((char*)pbRestoredMsg)
			SHOW_INFO("\".");

			START_OP("Verify the message.");
			if(0 == memcmp(pbMsg, pbRestoredMsg, ulRestoredMsgLen))
				CHECK_OP(CKR_OK)
			else
				SHOW_INFO("....[FAILED]\n")

			// Remove the RSA key pair from the ePass1000.
			START_OP("Proceed to remove the RSA key pair from the token.");
			rv = C_DestroyObject(hSession, hPubKey);
			CHECK_OP(rv);

			START_OP("Remove private key obeject.");
			rv = C_DestroyObject(hSession, hPriKey);
			CHECK_OP(rv);

		}while (0);

		// Release the memory.
		if(pbCipherBuffer)
		{
			free(pbCipherBuffer);
			pbCipherBuffer = NULL;
		}
		if(pbRestoredMsg)
		{
			free(pbRestoredMsg);
			pbCipherBuffer = NULL;
		}
	}
}