// !! We strongly recommend that you read the "readme.txt" file for this sample.
// !! It contains important instructions on how to use the sample code

#include <windows.h>								// for windows programs
#include <stdio.h>									// for sprintf
#include <string.h>									// for memset
#include "dris.h"									// for DRIS definition

#define MY_SDSN         10101						// !!!! change this value to be the value of your SDSN (demo = 10101)
#define MY_PRODCODE     "DEMO"						// !!!! change this value to match the Product Code in the dongle

#ifdef _MSC_VER
  #pragma warning(disable:4996)							// to disable warnings about sprintf etc... means we can use the same source for all compilers
#endif

/* The sample code contains 11 functions. You will not need to use all these
functions in your code. Just use which ever functions are most appropriate
and customise in your own way. The sample code is just a guide. You should
implement the protection in a stronger way using the techniques described in
the "Increasing your Protection" chapter of the manual.

If you are using Dinkey Lite then you can only use 4 functions:
ProtCheck, ProtCheckWithAlg, ProtCheckEnc, ProtCheckWithAlgEnc
*/

// this file contains 10 protection-check functions.
int ProtCheck(void);								// a standard protection check
int ProtCheckWithAlg(void);							// a protection check & executing an Algorithm
int WriteBytes(void);								// a protection check & write data to the dongle
int ReadBytes(void);								// a protection check & read data from the dongle
int EncryptUserData(void);							// a protection check & encrypting data and then decrypting the data
// these functions are the same as the functions listed above but encrypting all parameters passed to our API
int ProtCheckEnc(void);								// a standard protection check
int ProtCheckWithAlgEnc(void);						// a protection check & executing an Algorithm
int WriteBytesEnc(void);							// a protection check & write data to the dongle
int ReadBytesEnc(void);								// a protection check & read data from the dongle
int EncryptUserDataEnc(void);						// a protection check & encrypting data and then decrypting the data
// this function displays the current network users
int DisplayNetUsers(DRIS *dris);

// useful routines that are called by our functions - implemented at the end of this file
void random_set(void *buffer, int length);
void DisplayError(int ret_code, int extended_error);

char g_szAppName[] = "test program";

// ***************************   WinMain  ***************************************

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdParam, int nCmdShow) {
	// call the function(s) of your choice here
	// by default I have chosen a standard protection check
	ProtCheck();
	return 0;
}


// *************************  10 protection check functions **********************************

// **************************  ProtCheck  ***************************************
int ProtCheck(void) {
	DRIS dris;
	int ret_code;

	// set the DRIS to have random values
	random_set(&dris, sizeof(DRIS));
	// then set the values we want to use
	memcpy(dris.header, "DRIS", 4);
	dris.size = sizeof(DRIS);
	dris.function = PROTECTION_CHECK;					// standard protection check
	dris.flags = 0;										// no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...

	ret_code = DDProtCheck(&dris, NULL);

	if (ret_code != 0) {
		DisplayError(ret_code, dris.ext_err);			// display messages for any errors that have occurred
		return ret_code;
	}

	// later in your code you can check other values in the DRIS...

	if (dris.sdsn != MY_SDSN) {
		MessageBox(NULL, "Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.", NULL, MB_OK);
		return -1;
	}

	if (strcmp(dris.prodcode, MY_PRODCODE) != 0) {
		MessageBox(NULL, "Incorrect Product Code! Please modify your source code so that MY_PRODCODE is set to be the Product Code in the dongle.", NULL, MB_OK);
		return -1;		
	}
	
	// later on in your program you can check the return code again
	if (dris.ret_code != 0) {
		MessageBox(NULL, "Dinkey Dongle protection error", NULL, MB_OK);
		return -1;
	}

	MessageBox(NULL, "It worked", g_szAppName, MB_OK);
	
	// if you are using a network dongle and you want to list the network users then use this function:
//	DisplayNetUsers(&dris);
	
	return 0;
}

// **************************  ProtCheckWithAlg  ******************************

// !!!! You should replace this function with the one generated by the
// "generate source code" button in Algorithm tab for DinkeyAdd (if you have specified an algorithm)
// or from DinkeyLook if you are using a Dinkey Lite dongle.
int MyAlgorithm(int a, int b, int c, int d, int e, int f, int g, int h) {
   return a + b + c + d + e + f + g + h;
}

// NB for this to work you must program at least "user algorithm" into the dongle
// NB We have used a very simple algorithm here (in the MyAlgorithm function). You must patch this with the
// sample code generated by DinkeyAdd for the algorithm that you are using.  For Lite models copy the sample
// code from DinkeyLook
int ProtCheckWithAlg(void) {
	DRIS dris;
	int ret_code;

	// set the DRIS to have random values
	random_set(&dris, sizeof(DRIS));
	// then set the values we want to use
	memcpy(dris.header, "DRIS", 4);
	dris.size = sizeof(DRIS);
	dris.function = EXECUTE_ALGORITHM;					// standard protection check & execute algorithm
	dris.flags = 0;										// no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
	dris.alg_number = 1;								// execute algorithm 1 (you do not need to specify this field if you are only using Lite models).
	// you should remove these entries if you are using Dinkey Lite so that algorithm arguments are random
	dris.var_a = 1;										// sample values
	dris.var_b = 2;
	dris.var_c = 3;
	dris.var_d = 4;
	dris.var_e = 5;
	dris.var_f = 6;
	dris.var_g = 7;
	dris.var_h = 8;

	ret_code = DDProtCheck(&dris, NULL);

	if (ret_code != 0) {
		DisplayError(ret_code, dris.ext_err);			// display messages for any errors that have occurred
		return ret_code;
	}

	// later in your code you can check other values in the DRIS...

	if (dris.sdsn != MY_SDSN) {
		MessageBox(NULL, "Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.", NULL, MB_OK);
		return -1;
	}

	// ...later on in your program you can check the return code again
	if (dris.ret_code != 0) {
		MessageBox(NULL, "Dinkey Dongle protection error", NULL, MB_OK);
		return -1;
	}

	// if you want you can check the algorithm answer - however if algorithm is from your code then best to just use the answer in your code
	// !!!! Make sure you have replaced the MyAlgorithm routine with the algorithm you have specified in DinkeyAdd
	if (MyAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h) != dris.alg_answer) {
		MessageBox(NULL, "Dinkey protection error!\nYou have not patched your algorithm in the MyAlgorithm routine", NULL, MB_OK);
		return -1;
	}

	MessageBox(NULL, "It worked", g_szAppName, MB_OK);
	return 0;
}

// **************************  WriteBytes  ******************************
// This writes the Data "Hello, World" to the dongle data area at offset 7
// In order for this function to work you will need to have a data area in your dongle that is at least 21 bytes long.
int WriteBytes(void) {
	DRIS dris;
	char szDataToWrite[] = "Hello, World!";
	int ret_code;

	// set the DRIS to have random values
	random_set(&dris, sizeof(DRIS));
	// then set the values we want to use
	memcpy(dris.header, "DRIS", 4);
	dris.size = sizeof(DRIS);
	dris.function = WRITE_DATA_AREA;					// standard protection check and write data
	dris.flags = 0;										// no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
	dris.rw_offset = 7;
	dris.rw_length = sizeof(szDataToWrite);
	dris.rw_data_ptr = szDataToWrite;

	ret_code = DDProtCheck(&dris, NULL);

	if (ret_code != 0) {
		DisplayError(ret_code, dris.ext_err);			// display messages for any errors that have occurred
		return ret_code;
	}

	// ...later in your code you can check other values in the DRIS...

	if (dris.sdsn != MY_SDSN) {
		MessageBox(NULL, "Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.", NULL, MB_OK);
		return -1;
	}

	// later on in your program you can check the return code again
	if (dris.ret_code != 0) {
		MessageBox(NULL, "Dinkey Dongle protection error", NULL, MB_OK);
		return -1;
	}

	MessageBox(NULL, "It worked", g_szAppName, MB_OK);
	return 0;
}


// ************************  ReadBytes  ************************************
// This reads 14 bytes of data from offset 7 in the dongle data area
// In order for this function to work you will need to have a data area in your dongle that is at least 21 bytes long.
// In order for the data to be displayed properly by this routine it will need to be text data
int ReadBytes(void) {
	DRIS dris;
	char szDataRead[14], szBuffer[200];
	int ret_code;

	// set the DRIS to have random values
	random_set(&dris, sizeof(DRIS));
	// then set the values we want to use
	memcpy(dris.header, "DRIS", 4);
	dris.size = sizeof(DRIS);
	dris.function = READ_DATA_AREA;						// standard protection check & read data
	dris.flags = 0;										// no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
	dris.rw_offset = 7;
	dris.rw_length = sizeof(szDataRead)-1;
	dris.rw_data_ptr = szDataRead;

	ret_code = DDProtCheck(&dris, NULL);

	if (ret_code != 0) {
		DisplayError(ret_code, dris.ext_err);			// display messages for any errors that have occurred
		return ret_code;
	}

	// ...later in your code you can check other values in the DRIS...

	if (dris.sdsn != MY_SDSN) {
		MessageBox(NULL, "Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.", NULL, MB_OK);
		return -1;
	}

	// later on in your program you can check the return code again
	if (dris.ret_code != 0) {
		MessageBox(NULL, "Dinkey Dongle protection error", NULL, MB_OK);
		return -1;
	}

	szDataRead[sizeof(szDataRead)-1] = 0;				// null - terminate string so it displays OK. NB we assume that we are reading text that can be displayed
	sprintf(szBuffer, "It worked! Dinkey Dongle Data area, offset 7 is: %s", szDataRead);
	MessageBox(NULL, szBuffer, g_szAppName, MB_OK);
	return 0;
}


// **************************  EncryptUserData  ************************************
// This function will do a protection check and ask the dongle to encrypt some data using encryption key 1
// It will then do another protection check & decrypt the data
int EncryptUserData(void) {
	DRIS dris;
	char szBuffer[100];
	char szData[16] = "Hello, World!";					// pad message so it is multiple of 8-bytes long i.e. make array 16 bytes long (improves encryption)
	int ret_code;

	// set the DRIS to have random values
	random_set(&dris, sizeof(DRIS));
	// then set the values we want to use
	memcpy(dris.header, "DRIS", 4);
	dris.size = sizeof(DRIS);
	dris.function = ENCRYPT_USER_DATA;					// standard protection check & encrypt user data
	dris.flags = 0;										// no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
	dris.data_crypt_key_num = 1;
	dris.rw_length = sizeof(szData);
	dris.rw_data_ptr = szData;

	ret_code = DDProtCheck(&dris, NULL);

	if (ret_code != 0) {
		DisplayError(ret_code, dris.ext_err);			// display messages for any errors that have occurred
		return ret_code;
	}

	// ... could add code to check other elements of the DRIS, e.g. ret_code, sdsn, ...

	// Now decrypt the same data to get the original values
	random_set(&dris, sizeof(DRIS));
	// then set the values we want to use
	memcpy(dris.header, "DRIS", 4);
	dris.size = sizeof(DRIS);
	dris.function = DECRYPT_USER_DATA;					// standard protection check & decrypt user data
	dris.flags = 0;										// no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
	dris.data_crypt_key_num = 1;
	dris.rw_length = sizeof(szData);
	dris.rw_data_ptr = szData;

	ret_code = DDProtCheck(&dris, NULL);

	if (ret_code != 0) {
		DisplayError(ret_code, dris.ext_err);			// display messages for any errors that have occurred
		return ret_code;
	}

	// ...later in your code you can check other values in the DRIS...

	if (dris.sdsn != MY_SDSN) {
		MessageBox(NULL, "Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.", NULL, MB_OK);
		return -1;
	}

	// later on in your program you can check the return code again
	if (dris.ret_code != 0) {
		MessageBox(NULL, "Dinkey Dongle protection error", NULL, MB_OK);
		return -1;
	}

	sprintf(szBuffer, "It worked. Data decrypted is: %s", szData);
	MessageBox(NULL, szBuffer, g_szAppName, MB_OK);
	return 0;
}


// !!!!!!!!!!!   all the functions listed below encrypt all parameters to our API  !!!!!!!!!!!!!!!!!!!!!
// In order for them to work properly you need to choose "Encrypt DRIS" and "Encrypt Data passed to API"
// in the Extra Security tab of DinkeyAdd. In this example, for Data Encryption I use the r/w algorithm
// but the code can be modified easily to use the 3 encryption parameters.

// !!!! please overwrite this function with the one generated by DinkeyAdd for R/W Algorithm
int MyRWAlgorithm(int a, int b, int c, int d, int e, int f, int g, int h) {
   return a ^ b ^ c ^ d ^ e ^ f;
}

// !!!! please overwrite this function with the one generated by DinkeyAdd
void CryptDRIS(DRIS *dris, unsigned int seed1, unsigned int seed2) {
	int i, j, k;
	unsigned char bigseed[256], S[256];
	unsigned char temp, t;

	for (i=0; i<256; i+=8) {
		memcpy(bigseed+i, &seed1, 4);
		memcpy(bigseed+i+4, &seed2, 4);
	}
	for (i=0; i<256; i++)
		S[i] = i;
	for (i=0, j=0; i<256; i++) {
		j = (j + S[i] + bigseed[i] + 123) % 256;
		temp = S[i];
		S[i] = S[j];
		S[j] = temp;
	}
	for (i=0, j=0, k=16; k<(int)sizeof(DRIS); k++) {
		i = (i + 1) % 256;
		j = (j + S[i] + 212) % 256;
		temp = S[i];
		S[i] = S[j];
		S[j] = temp;
		t = (S[i] + S[j] + 97) % 256;
		((unsigned char *)dris)[k] ^= S[t];
	}
	return;
}

// !!!! please overwrite this function with the one generated by DinkeyAdd
void CryptApiData(unsigned char *data, int length, unsigned int seed1, unsigned int seed2, int alg_answer) {
	int i, j, k;
	unsigned char bigseed[256], S[256];
	unsigned char temp, t;

	for (i=0; i<256; i+=8) {
		memcpy(bigseed+i, &seed1, 4);
		memcpy(bigseed+i+4, &seed2, 4);
	}
	for (i=0; i<256; i++)
		S[i] = i;
	for (i=0, j=0; i<256; i++) {
		j = (j + S[i] + bigseed[i] + (alg_answer & 0xFF)) % 256;
		temp = S[i];
		S[i] = S[j];
		S[j] = temp;
	}
	for (i=0, j=0, k=0; k<length; k++) {
		i = (i + 1) % 256;
		j = (j + S[i] + ((alg_answer >> 8) & 0xFF)) % 256;
		temp = S[i];
		S[i] = S[j];
		S[j] = temp;
		t = (S[i] + S[j] + ((alg_answer >> 16) & 0xFF)) % 256;
		data[k] ^= S[t];
	}
	return;
}

// ****************************   ProtCheckEnc  ***************************
// We encrypt the DRIS passed to the API. Patch the CryptDRIS function in this file from source code generated by DinkeyAdd.
// NB if you are using the debug module then do not call CryptDRIS as DRIS encryption is not supported. See notes in C readme.txt
int ProtCheckEnc(void) {
	DRIS dris;
	int ret_code;

	// set the DRIS to have random values
	random_set(&dris, sizeof(DRIS));					// NB this also fills seed1, seed2 with random values
	// then set the values we want to use
	memcpy(dris.header, "DRIS", 4);
	dris.size = sizeof(DRIS);
	dris.function = PROTECTION_CHECK;					// standard protection check
	dris.flags = 0;										// no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...

	CryptDRIS(&dris, dris.seed1, dris.seed2);			// encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	ret_code = DDProtCheck(&dris, NULL);

	CryptDRIS(&dris, dris.seed1, dris.seed2);			// decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	if (ret_code != 0) {
		DisplayError(ret_code, dris.ext_err);			// display messages for any errors that have occurred
		return ret_code;
	}

	// later in your code you can check other values in the DRIS...

	if (dris.sdsn != MY_SDSN) {
		MessageBox(NULL, "Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.", NULL, MB_OK);
		return -1;
	}

	if (strcmp(dris.prodcode, MY_PRODCODE) != 0) {
		MessageBox(NULL, "Incorrect Product Code! Please modify your source code so that MY_PRODCODE is set to be the Product Code in the dongle.", NULL, MB_OK);
		return -1;		
	}

	// later on in your program you can check the return code again
	if (dris.ret_code != 0) {
		MessageBox(NULL, "Dinkey Dongle protection error", NULL, MB_OK);
		return -1;
	}

	MessageBox(NULL, "It worked", g_szAppName, MB_OK);
	return 0;
}

// *************************  ProtCheckWithAlgEnc  *****************************
// NB for this to work you must program at least "user algorithm" into the dongle
// NB We have used a very simple algorithm here (in the MyAlgorithm function). You must patch this with the
// sample code generated by DinkeyAdd for the algorithm that you are using. For Lite models copy the sample code from DinkeyLook.
// We encrypt the DRIS passed to the API. Patch the CryptDRIS function in this file from source code generated by DinkeyAdd.
int ProtCheckWithAlgEnc(void) {
	DRIS dris;
	int ret_code;

	// set the DRIS to have random values
	random_set(&dris, sizeof(DRIS));					// NB this also fills seed1, seed2 with random values
	// then set the values we want to use
	memcpy(dris.header, "DRIS", 4);
	dris.size = sizeof(DRIS);
	dris.function = EXECUTE_ALGORITHM;					// standard protection check & execute algorithm
	dris.flags = 0;										// no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
	dris.alg_number = 1;								// execute algorithm 1 (you do not need to specify this field if you are only using Lite models).
	// you should remove these entries if you are using Dinkey Lite so that algorithm arguments are random
	dris.var_a = 1;										// sample values
	dris.var_b = 2;
	dris.var_c = 3;
	dris.var_d = 4;
	dris.var_e = 5;
	dris.var_f = 6;
	dris.var_g = 7;
	dris.var_h = 8;

	CryptDRIS(&dris, dris.seed1, dris.seed2);			// encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	ret_code = DDProtCheck(&dris, NULL);

	CryptDRIS(&dris, dris.seed1, dris.seed2);			// decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	if (ret_code != 0) {
		DisplayError(ret_code, dris.ext_err);			// display messages for any errors that have occurred
		return ret_code;
	}

	// later in your code you can check other values in the DRIS...

	if (dris.sdsn != MY_SDSN) {
		MessageBox(NULL, "Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.", NULL, MB_OK);
		return -1;
	}

	// ...later on in your program you can check the return code again
	if (dris.ret_code != 0) {
		MessageBox(NULL, "Dinkey Dongle protection error", NULL, MB_OK);
		return -1;
	}

	// if you want you can check the algorithm answer - however if algorithm is from your code then best to just use the answer in your code
	// !!!! Make sure you have replaced the MyAlgorithm routine with the algorithm you have specified in DinkeyAdd
	if (MyAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h) != dris.alg_answer) {
		MessageBox(NULL, "Dinkey protection error!\nYou have not patched your algorithm in the MyAlgorithm routine", NULL, MB_OK);
		return -1;
	}

	MessageBox(NULL, "It worked", g_szAppName, MB_OK);
	return 0;
}

// ****************************  WriteBytesEnc  ********************************
// This writes the Data "Hello, World" to the dongle data area at offset 7
// In order for this function to work you will need to have a data area in your dongle that is at least 21 bytes long.
// We encrypt the DRIS and Data passed to the API. Patch the CryptDRIS and CryptApiData functions in this file from source code generated by DinkeyAdd.
int WriteBytesEnc(void) {
	DRIS dris;
	char szDataToWrite[] = "Hello, World!";
	int ret_code, alg_ans;

	// set the DRIS to have random values
	random_set(&dris, sizeof(DRIS));					// NB this also fills seed1, seed2, var_a...var_h with random values
	// then set the values we want to use
	memcpy(dris.header, "DRIS", 4);
	dris.size = sizeof(DRIS);
	dris.function = WRITE_DATA_AREA;					// standard protection check & write data
	dris.flags = 0;										// no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
	dris.rw_offset = 7;
	dris.rw_length = sizeof(szDataToWrite);
	dris.rw_data_ptr = szDataToWrite;

	// calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm
	alg_ans = MyRWAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h);

	// encrypt data we want to write.
	CryptApiData((BYTE *)szDataToWrite, sizeof(szDataToWrite), dris.seed1, dris.seed2, alg_ans);

	CryptDRIS(&dris, dris.seed1, dris.seed2);			// encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	ret_code = DDProtCheck(&dris, NULL);

	CryptDRIS(&dris, dris.seed1, dris.seed2);			// decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	if (ret_code != 0) {
		DisplayError(ret_code, dris.ext_err);			// display messages for any errors that have occurred
		return ret_code;
	}

	// ...later in your code you can check other values in the DRIS...

	if (dris.sdsn != MY_SDSN) {
		MessageBox(NULL, "Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.", NULL, MB_OK);
		return -1;
	}

	// later on in your program you can check the return code again
	if (dris.ret_code != 0) {
		MessageBox(NULL, "Dinkey Dongle protection error", NULL, MB_OK);
		return -1;
	}

	MessageBox(NULL, "It worked", g_szAppName, MB_OK);
	return 0;
}

// ****************************  ReadBytesEnc  *********************************
// This reads 14 bytes of data from offset 7 in the dongle data area
// In order for this function to work you will need to have a data area in your dongle that is at least 21 bytes long.
// In order for the data to be displayed properly by this routine it will need to be text data
// We encrypt the DRIS and Data passed from the API. Patch the CryptDRIS and CryptApiData functions in this file from source code generated by DinkeyAdd.
int ReadBytesEnc(void) {
	DRIS dris;
	char szDataRead[14], szBuffer[200];
	int ret_code, alg_ans;

	// set the DRIS to have random values
	random_set(&dris, sizeof(DRIS));					// NB this also fills seed1, seed2, var_a...var_h with random values
	// then set the values we want to use
	memcpy(dris.header, "DRIS", 4);
	dris.size = sizeof(DRIS);
	dris.function = READ_DATA_AREA;						// standard protection check & read data
	dris.flags = 0;										// no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
	dris.rw_offset = 7;
	dris.rw_length = sizeof(szDataRead)-1;
	dris.rw_data_ptr = szDataRead;

	// calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm
	alg_ans = MyRWAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h);

	CryptDRIS(&dris, dris.seed1, dris.seed2);			// encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	ret_code = DDProtCheck(&dris, NULL);

	CryptDRIS(&dris, dris.seed1, dris.seed2);			// decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	if (ret_code != 0) {
		DisplayError(ret_code, dris.ext_err);			// display messages for any errors that have occurred
		return ret_code;
	}

	// ...later in your code you can check other values in the DRIS...

	if (dris.sdsn != MY_SDSN) {
		MessageBox(NULL, "Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.", NULL, MB_OK);
		return -1;
	}

	// later on in your program you can check the return code again
	if (dris.ret_code != 0) {
		MessageBox(NULL, "Dinkey Dongle protection error", NULL, MB_OK);
		return -1;
	}

	// decrypt data that was read.
	CryptApiData((BYTE *)szDataRead, sizeof(szDataRead)-1, dris.seed1, dris.seed2, alg_ans);

	szDataRead[sizeof(szDataRead)-1] = 0;				// null - terminate string so it displays OK. NB we assume that we are reading text that can be displayed
	sprintf(szBuffer, "It worked! Dinkey Dongle Data area, offset 7 is: %s", szDataRead);
	MessageBox(NULL, szBuffer, g_szAppName, MB_OK);
	return 0;
}

// *************************  EncryptUserDataEnc  ******************************
// This function will do a protection check and ask the dongle to encrypt some data using encryption key 1
// It will then do another protection & decrypt the data
// We encrypt the DRIS and Data passed to/from the API. Patch the CryptDRIS and CryptApiData functions in this file from source code generated by DinkeyAdd.
int EncryptUserDataEnc(void) {
	DRIS dris;
	char szBuffer[100];
	char szData[16] = "Hello, World!";					// pad message so it is multiple of 8-bytes long i.e. make array 16 bytes long (improves encryption)
	int ret_code, alg_ans;

	// set the DRIS to have random values
	random_set(&dris, sizeof(DRIS));
	// then set the values we want to use
	memcpy(dris.header, "DRIS", 4);
	dris.size = sizeof(DRIS);
	dris.function = ENCRYPT_USER_DATA;					// standard protection check & encrypt user data
	dris.flags = 0;										// no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
	dris.data_crypt_key_num = 1;
	dris.rw_length = sizeof(szData);
	dris.rw_data_ptr = szData;

	// calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm
	alg_ans = MyRWAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h);

	// encrypt data we want to pass.
	CryptApiData((BYTE *)szData, sizeof(szData), dris.seed1, dris.seed2, alg_ans);

	CryptDRIS(&dris, dris.seed1, dris.seed2);			// encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	ret_code = DDProtCheck(&dris, NULL);

	CryptDRIS(&dris, dris.seed1, dris.seed2);			// decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	if (ret_code != 0) {
		DisplayError(ret_code, dris.ext_err);			// display messages for any errors that have occurred
		return ret_code;
	}

	// ... could add code to check other elements of the DRIS, e.g. ret_code, sdsn, ...

	// Now decrypt the same data to get the original values
	random_set(&dris, sizeof(DRIS));
	// then set the values we want to use
	memcpy(dris.header, "DRIS", 4);
	dris.size = sizeof(DRIS);
	dris.function = DECRYPT_USER_DATA;					// standard protection check & decrypt user data
	dris.flags = 0;										// no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
	dris.data_crypt_key_num = 1;
	dris.rw_length = sizeof(szData);
	dris.rw_data_ptr = szData;

	CryptDRIS(&dris, dris.seed1, dris.seed2);			// encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	ret_code = DDProtCheck(&dris, NULL);

	CryptDRIS(&dris, dris.seed1, dris.seed2);			// decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	// ...later in your code you can check other values in the DRIS...
	if (ret_code != 0) {
		DisplayError(ret_code, dris.ext_err);			// display messages for any errors that have occurred
		return ret_code;
	}

	if (dris.sdsn != MY_SDSN) {
		MessageBox(NULL, "Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.", NULL, MB_OK);
		return -1;
	}

	// later on in your program you can check the return code again
	if (dris.ret_code != 0) {
		MessageBox(NULL, "Dinkey Dongle protection error", NULL, MB_OK);
		return -1;
	}

	alg_ans = MyRWAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h);
	// decrypt data that was passed to us by the API
	CryptApiData((BYTE *)szData, sizeof(szData), dris.seed1, dris.seed2, alg_ans);

	sprintf(szBuffer, "It worked. Data decrypted is: %s", szData);
	MessageBox(NULL, szBuffer, g_szAppName, MB_OK);
	return 0;
}

// *********************  DisplayNetUsers   ***********************
// This function displays the current network users (for DinkeyNet only).
// The DDProtCheck function must be called before this function is called.
// (Normally you would use DinkeyServer to view the network users but this enables you to do it from the client if you want) 

char net_user_display_msg[] = "%d. licence: %s, user: %s, computer: %s, ipaddress: %s\n";
#define MAX_MESSAGE_SIZE (sizeof(NU_INFO) + sizeof(net_user_display_msg))

// !!!! NB You should check for errors when dynamic allocating memory
int DisplayNetUsers(DRIS *dris) {
	int ret_code, extended_error, max_net_users, num_net_users, i;
	char szBuffer[MAX_MESSAGE_SIZE];
	char *szMsg;
	NU_INFO *nu_info;

	if (dris->net_users == -1)
		max_net_users = 100;			// if unlimited network users are allowed then display up to 100 users (for example)
	else
		max_net_users = dris->net_users;

	if (max_net_users == 0)				// no network users are allowed, so nothing to do!
		return 0;

	if (dris->model < 3) {
		MessageBox(NULL, "A network dongle has not been detected. Therefore you cannot display the network users.", "Net Users", MB_OK);
		return -1;
	}

	// allocate memory for array of network user information structures and also to display the message to users
    nu_info = (NU_INFO *)malloc(sizeof(NU_INFO) * max_net_users);
	szMsg = (char *)malloc(MAX_MESSAGE_SIZE * max_net_users);

	// get network user information
	ret_code = DDGetNetUserList(NULL, &num_net_users, nu_info, max_net_users, &extended_error);

	if (ret_code != 0) {
		sprintf(szMsg, "Error %d/%d finding network user information.", ret_code, extended_error);
		MessageBox(NULL, szMsg, "Net Users", MB_OK);
	}
	else {
		sprintf(szMsg, "number of net users: %d\n", num_net_users);
		for (i=0; i<num_net_users; i++) {
			sprintf(szBuffer, net_user_display_msg, i+1, nu_info[i].licenceName, nu_info[i].userName, nu_info[i].computerName, nu_info[i].ipAddress);
			strcat(szMsg, szBuffer);
		}
		MessageBox(NULL, szMsg, "Net Users", MB_OK);
	}
	free(szMsg);
	free(nu_info);
	return ret_code;
}

// *************  useful routines that are called from our functions  **********

// fills the buffer with length bytes of random data
// NB there are better ways of doing this (for example with the Windows Crypto API calls)
void random_set(void *buffer, int length) {
	int i;

	srand(GetTickCount());
	for (i=0; i<length; i++)
		((unsigned char *)buffer)[i] = (unsigned char)rand();

	return;
}

// displays messages for the most common errors. You will want to change this for your code
void DisplayError(int ret_code, int extended_error) {
	char szBuffer[200];

	switch (ret_code) {
	case 401:
		MessageBox(NULL, "Error! No dongles detected!", g_szAppName, MB_OK);
		break;

	case 403:
		MessageBox(NULL, "Error! The dongle detected has a different type to the one specified in DinkeyAdd.", g_szAppName, MB_OK);
		break;

	case 404:
		MessageBox(NULL, "Error! The dongle detected has a different model to those specified in DinkeyAdd.", g_szAppName, MB_OK);
		break;

	case 409:
		MessageBox(NULL, "Error! The dongle detected has not been programmed by DinkeyAdd.", g_szAppName, MB_OK);
		break;

	case 410:
		MessageBox(NULL, "Error! The dongle detected has a different Product Code to the one specified in DinkeyAdd.", g_szAppName, MB_OK);
		break;

	case 411:
		MessageBox(NULL, "Error! The dongle detected does not contain the licence associated with this program.", g_szAppName, MB_OK);
		break;

	case 413:
		MessageBox(NULL, "Error! This program has not been protected by DinkeyAdd. For guidance please read the DinkeyAdd chapter of the Dinkey manual.", g_szAppName, MB_OK);
		break;

	case 417:
		MessageBox(NULL, "Error! One or more of the parameters set in the DRIS is incorrect. This could be caused if you are encrypting the DRIS in your code but did not specify DRIS encryption in DinkeyAdd - or vice versa.", g_szAppName, MB_OK);
		break;

	case 423:
		MessageBox(NULL, "Error! The number of network users has been exceeded.", g_szAppName, MB_OK);
		break;

	case 435:
		MessageBox(NULL, "Error! DinkeyServer has not been detected on the network.", g_szAppName, MB_OK);
		break;

	case 922:
		MessageBox(NULL, "Error! The Software Key has expired.", g_szAppName, MB_OK);
		break;
		
	default:
		sprintf(szBuffer, "An error occurred checking the dongle.\nError: %d, Extended Error: %d", ret_code, extended_error);
		MessageBox(NULL, szBuffer, g_szAppName, MB_OK);
		break;
	}
	return;
}
