from libc.string cimport memcpy
from libc.string cimport strlen
import random

# The sample code contains 10 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.
# ProtCheck                           a standard protection check
# ProtCheckWithAlg                    a protection check & executing an Algorithm
# WriteBytes                          a protection check & write data to the dongle
# ReadBytes                           a protection check & read data from the dongle
# EncryptUserData                     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
# ProtCheckEnc                        a standard protection check
# ProtCheckWithAlgEnc                 a protection check & executing an Algorithm
# WriteBytesEnc                       a protection check & write data to the dongle
# ReadBytesEnc                        a protection check & read data from the dongle
# EncryptUserDataEnc                  a protection check & encrypting data and then decrypting the data

DEF MY_SDSN = 10101                 # !!!! modify this value to your SDSN
DEF MY_PRODCODE = b'DEMO'           # !!!! change this value to match the Product Code in the dongle

# for some reason these constants have to be declared here rather than the pxd file
# functions - must specify only one
DEF PROTECTION_CHECK=1				# checks for dongle, check program params...
DEF EXECUTE_ALGORITHM=2				# protection check + calculate answer for specified algorithm with specified inputs
DEF WRITE_DATA_AREA=3				# protection check + writes dongle data area
DEF READ_DATA_AREA=4				# protection check + reads dongle data area
DEF ENCRYPT_USER_DATA=5				# protection check + the dongle will encrypt user data
DEF DECRYPT_USER_DATA=6				# protection check + the dongle will decrypt user data
DEF FAST_PRESENCE_CHECK=7			# checks for the presence of the correct dongle only with minimal security, no flags allowed.
DEF STOP_NET_USER=8					# stops a network user (a protection check is NOT performed)

# flags - can specify as many as you like
DEF DEC_ONE_EXEC=1					# decrement execs by 1 
DEF DEC_MANY_EXECS=2				# decrement execs by number specified in execs_decrement
DEF START_NET_USER=4				# starts a network user
DEF USE_FUNCTION_ARGUMENT=16		# use the extra argument in the function for pointers
DEF CHECK_LOCAL_FIRST=32			# always look in local ports before looking in network ports
DEF CHECK_NETWORK_FIRST=64			# always look on the network before looking in local ports
DEF USE_ALT_PROG_NAME=128			# use name specified in prog_name instead of this program name
DEF DONT_SET_MAXDAYS_EXPIRY=256		# if the max days expiry date has not been calculated then do not do it this time
DEF MATCH_DONGLE_NUMBER=512			# restrict the search to match the dongle number specified in the DRIS
DEF DONT_RETURN_FD_DRIVE=1024       # if an FD dongle has been detected then don't return the flash drive/mount name

# *************************  our 10 functions **********************************

# **************************  ProtCheck  ***************************************
cpdef int ProtCheck():
	cdef DRIS dris
	cdef int ret_code

	# set the DRIS to have random values
	cdef unsigned char *drisbuf = <unsigned char*>&dris
	for i in range(sizeof(DRIS)): 
		drisbuf[i]= <unsigned char>random.randint(0,255)
	# then set the values we want to use
	memcpy(dris.header, b'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)
		return ret_code

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

	if (dris.sdsn != MY_SDSN):
		print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
		return -1

	if (dris.prodcode != MY_PRODCODE):
		print("Incorrect Product Code! Please modify your source code so that MY_PRODCODE is set to be the Product Code in the dongle.")
		return -1

	# later on in your program you can check the return code again
	if (dris.ret_code != 0):
		print("Dinkey Dongle protection error")
		return -1

	print("It worked!")
	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.
# (If it does not generate source code for Python you have translate it from C)
cdef 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
cpdef int ProtCheckWithAlg():
	cdef DRIS dris
	cdef int ret_code

	# set the DRIS to have random values
	cdef unsigned char *drisbuf = <unsigned char*>&dris
	for i in range(sizeof(DRIS)): 
		drisbuf[i]= <unsigned char>random.randint(0,255)
	# then set the values we want to use
	memcpy(dris.header, b'DRIS', 4)
	dris.size = sizeof(DRIS)
	dris.function = EXECUTE_ALGORITHM					# standard protection check and 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)

	ret_code = DDProtCheck(&dris, NULL)

	if (ret_code != 0):
		DisplayError(ret_code, dris.ext_err)
		return ret_code

	if (dris.sdsn != MY_SDSN):
		print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
		return -1

	# later on in your program you can check the return code again
	if (dris.ret_code != 0):
		print("Dinkey Dongle protection error")
		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):
		print("Dinkey protection error!\nYou have not patched your algorithm in the MyAlgorithm routine")
		return -1

	print("It worked!")
	return 0


# **************************  WriteBytes  ******************************
# This writes the string "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.
cpdef int WriteBytes():
	cdef DRIS dris
	cdef int ret_code
	cdef char *datatowrite = "Hello, World!"

	# set the DRIS to have random values
	cdef unsigned char *drisbuf = <unsigned char*>&dris
	for i in range(sizeof(DRIS)): 
		drisbuf[i]= <unsigned char>random.randint(0,255)
	# then set the values we want to use
	memcpy(dris.header, b'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 = <int>strlen(datatowrite)+1			# include the null
	dris.rw_data_ptr = datatowrite

	ret_code = DDProtCheck(&dris, NULL)

	if (ret_code != 0):
		DisplayError(ret_code, dris.ext_err)
		return ret_code

	if (dris.sdsn != MY_SDSN):
		print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
		return -1

	# later on in your program you can check the return code again
	if (dris.ret_code != 0):
		print("Dinkey Dongle protection error")
		return -1

	print("It worked!")
	return 0

# ************************  ReadBytes  ************************************
# This reads 13 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
cpdef ReadBytes():
	cdef DRIS dris
	cdef int ret_code
	cdef char datatoread[14]

	# set the DRIS to have random values
	cdef unsigned char *drisbuf = <unsigned char*>&dris
	for i in range(sizeof(DRIS)): 
		drisbuf[i]= <unsigned char>random.randint(0,255)
	# then set the values we want to use
	memcpy(dris.header, b'DRIS', 4)
	dris.size = sizeof(DRIS)
	dris.function = READ_DATA_AREA						# standard protection check and 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(datatoread)
	dris.rw_data_ptr = datatoread

	ret_code = DDProtCheck(&dris, NULL)

	if (ret_code != 0):
		DisplayError(ret_code, dris.ext_err)
		return ret_code

	if (dris.sdsn != MY_SDSN):
		print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
		return -1

	# later on in your program you can check the return code again
	if (dris.ret_code != 0):
		print("Dinkey Dongle protection error")
		return -1

	print("It worked! Dinkey Dongle Data area, offset 7 is: " + str(datatoread))
	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
cpdef EncryptUserData():
	cdef DRIS dris
	cdef int ret_code
	cdef char *mydata = "Hello, World!"
	cdef int data_length = <int>strlen(mydata)+1		# include the NULL
	
	# set the DRIS to have random values
	cdef unsigned char *drisbuf = <unsigned char*>&dris
	for i in range(sizeof(DRIS)): 
		drisbuf[i]= <unsigned char>random.randint(0,255)
	# then set the values we want to use
	memcpy(dris.header, b'DRIS', 4)
	dris.size = sizeof(DRIS)
	dris.function = ENCRYPT_USER_DATA					# standard protection check and encrypt 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 = data_length
	dris.rw_data_ptr = mydata

	ret_code = DDProtCheck(&dris, NULL)

	if (ret_code != 0):
		DisplayError(ret_code, dris.ext_err)
		return ret_code

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

	# Now decrypt this (encrypted) data to get the original values
	for i in range(sizeof(DRIS)): 
		drisbuf[i]= <unsigned char>random.randint(0,255)
	memcpy(dris.header, b'DRIS', 4)
	dris.size = sizeof(DRIS)
	dris.function = DECRYPT_USER_DATA					# standard protection check and decrypt 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 = data_length
	dris.rw_data_ptr = mydata

	ret_code = DDProtCheck(&dris, NULL)

	if (ret_code != 0):
		DisplayError(ret_code, dris.ext_err)
		return ret_code

	print("It worked! Data decrypted is " + str(mydata))
	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
# NB DinkeyAdd currently does not generate code for Python so you will have to translate it from C
cdef 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

# !!!! modify parameters 1,2 and 3 to match those specified in DinkeyAdd
cdef void CryptDRIS(DRIS *dris, unsigned int seed1, unsigned int seed2):
	cdef int i, j, k, t
	cdef unsigned char bigseed[256]
	cdef unsigned char S[256]
	cdef unsigned char temp

	for i in range(0, 256, 8):
		memcpy(bigseed+i, &seed1, 4)
		memcpy(bigseed+i+4, &seed2, 4)
	for i in range(256):
		S[i] = i
	j = 0
	for i in range(256):
		j = (j + S[i] + bigseed[i] + 123) % 256					# parameter 1
		temp = S[i]
		S[i] = S[j]
		S[j] = temp
	i = 0
	j = 0
	for k in range(16, <int>sizeof(DRIS)):
		i = (i + 1) % 256
		j = (j + S[i] + 212) % 256								# parameter 2
		temp = S[i]
		S[i] = S[j]
		S[j] = temp
		t = (S[i] + S[j] + 97) % 256							# parameter 3
		(<unsigned char *>dris)[k] ^= S[t]
	return

# !!!! modify parameters 1,2 and 3 to match those specified in DinkeyAdd (if you specified r/w algorithm then no need to modify anything)
cdef void CryptApiData(unsigned char *mydata, int length, unsigned int seed1, unsigned int seed2, int alg_answer):
	cdef int i, j, k, t
	cdef unsigned char bigseed[256]
	cdef unsigned char S[256]
	cdef unsigned char temp

	for i in range(0, 256, 8):
		memcpy(bigseed+i, &seed1, 4)
		memcpy(bigseed+i+4, &seed2, 4)
	for i in range(256):
		S[i] = i
	j = 0
	for i in range(256):
		j = (j + S[i] + bigseed[i] + (alg_answer & 0xff)) % 256			# parameter1
		temp = S[i]
		S[i] = S[j]
		S[j] = temp
	i = 0
	j = 0
	for k in range(length):
		i = (i + 1) % 256
		j = (j + S[i] + ((alg_answer >> 8) & 0xff)) % 256				# parameter 2
		temp = S[i]
		S[i] = S[j]
		S[j] = temp
		t = (S[i] + S[j] + ((alg_answer >> 16) & 0xff)) % 256			# parameter 3
		(<unsigned char *>mydata)[k] ^= S[t]
	return

# **************************  ProtCheckEnc  ***************************************
# We encrypt the DRIS passed to the API. Patch the CryptDRIS function in this file with parameters specified in 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
cpdef int ProtCheckEnc():
	cdef DRIS dris
	cdef int ret_code

	# set the DRIS to have random values
	cdef unsigned char *drisbuf = <unsigned char*>&dris
	for i in range(sizeof(DRIS)): 
		drisbuf[i]= <unsigned char>random.randint(0,255)
	# then set the values we want to use
	memcpy(dris.header, b'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)
		return ret_code

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

	if (dris.sdsn != MY_SDSN):
		print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
		return -1

	if (dris.prodcode != MY_PRODCODE):
		print("Incorrect Product Code! Please modify your source code so that MY_PRODCODE is set to be the Product Code in the dongle.")
		return -1

	# later on in your program you can check the return code again
	if (dris.ret_code != 0):
		print("Dinkey Dongle protection error")
		return -1

	print("It worked!")
	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 with parameters specified in DinkeyAdd
cpdef int ProtCheckWithAlgEnc():
	cdef DRIS dris
	cdef int ret_code

	# set the DRIS to have random values
	cdef unsigned char *drisbuf = <unsigned char*>&dris
	for i in range(sizeof(DRIS)): 
		drisbuf[i]= <unsigned char>random.randint(0,255)
	# then set the values we want to use
	memcpy(dris.header, b'DRIS', 4)
	dris.size = sizeof(DRIS)
	dris.function = EXECUTE_ALGORITHM					# standard protection check and 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)

	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)
		return ret_code

	if (dris.sdsn != MY_SDSN):
		print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
		return -1

	# later on in your program you can check the return code again
	if (dris.ret_code != 0):
		print("Dinkey Dongle protection error")
		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):
		print("Dinkey protection error!\nYou have not patched your algorithm in the MyAlgorithm routine")
		return -1

	print("It worked!")
	return 0


# **************************  WriteBytesEnc  ******************************
# This writes the string "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 with parameters specified in DinkeyAdd.
cpdef int WriteBytesEnc():
	cdef DRIS dris
	cdef int ret_code, alg_answer
	cdef char *datatowrite = "Hello, World!"
	cdef string_length = <int>strlen(datatowrite)+1		# include the null

	# set the DRIS to have random values
	cdef unsigned char *drisbuf = <unsigned char*>&dris
	for i in range(sizeof(DRIS)): 
		drisbuf[i]= <unsigned char>random.randint(0,255)
	# then set the values we want to use
	memcpy(dris.header, b'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 = string_length
	dris.rw_data_ptr = datatowrite

	# calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm specified in DinkeyAdd
	alg_answer = 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(<unsigned char *>datatowrite, string_length, dris.seed1, dris.seed2, alg_answer)

	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)
		return ret_code

	if (dris.sdsn != MY_SDSN):
		print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
		return -1

	# later on in your program you can check the return code again
	if (dris.ret_code != 0):
		print("Dinkey Dongle protection error")
		return -1

	print("It worked!")
	return 0

# ************************  ReadBytesEnc  ************************************
# This reads 13 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 to the API. Patch the CryptDRIS and CryptApiData functions in this file with parameters specified in DinkeyAdd.
cpdef ReadBytesEnc():
	cdef DRIS dris
	cdef int ret_code, alg_answer
	cdef char datatoread[14]

	# set the DRIS to have random values
	cdef unsigned char *drisbuf = <unsigned char*>&dris
	for i in range(sizeof(DRIS)): 
		drisbuf[i]= <unsigned char>random.randint(0,255)
	# then set the values we want to use
	memcpy(dris.header, b'DRIS', 4)
	dris.size = sizeof(DRIS)
	dris.function = READ_DATA_AREA						# standard protection check and 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(datatoread)
	dris.rw_data_ptr = datatoread

	# calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm specified in DinkeyAdd
	alg_answer = 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)
		return ret_code

	if (dris.sdsn != MY_SDSN):
		print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
		return -1

	# later on in your program you can check the return code again
	if (dris.ret_code != 0):
		print("Dinkey Dongle protection error")
		return -1

	# decrypt data that was read
	CryptApiData(<unsigned char *>datatoread, sizeof(datatoread), dris.seed1, dris.seed2, alg_answer)

	print("It worked! Dinkey Dongle Data area, offset 7 is: " + str(datatoread))
	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 check & decrypt the 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.
cpdef EncryptUserDataEnc():
	cdef DRIS dris
	cdef int ret_code, alg_answer
	cdef char *mydata = "Hello, World!"
	cdef int data_length = <int>strlen(mydata)+1		# include the NULL

	# set the DRIS to have random values
	cdef unsigned char *drisbuf = <unsigned char*>&dris
	for i in range(sizeof(DRIS)): 
		drisbuf[i]= <unsigned char>random.randint(0,255)
	# then set the values we want to use
	memcpy(dris.header, b'DRIS', 4)
	dris.size = sizeof(DRIS)
	dris.function = ENCRYPT_USER_DATA					# standard protection check and encrypt 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 = data_length
	dris.rw_data_ptr = mydata

	# calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm specified in DinkeyAdd
	alg_answer = 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(<unsigned char *>mydata, data_length, dris.seed1, dris.seed2, alg_answer)

	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)
		return ret_code

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

	# Now decrypt this (encrypted) data to get the original values
	for i in range(sizeof(DRIS)): 
		drisbuf[i]= <unsigned char>random.randint(0,255)
	memcpy(dris.header, b'DRIS', 4)
	dris.size = sizeof(DRIS)
	dris.function = DECRYPT_USER_DATA					# standard protection check and decrypt 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 = data_length
	dris.rw_data_ptr = mydata

	# calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm specified in DinkeyAdd
	alg_answer = 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)
		return ret_code

	# decrypt data that was pass to us by the API
	CryptApiData(<unsigned char *>mydata, data_length, dris.seed1, dris.seed2, alg_answer)

	print("It worked! Data decrypted is " + str(mydata))
	return 0


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

	if (ret_code == 401):
		print("Error! No dongles detected!")
	elif (ret_code == 403):
		print("Error! The dongle detected has a different type to the one specified in DinkeyAdd.")
	elif (ret_code == 404):
		print("Error! The dongle detected has a different model to those specified in DinkeyAdd.")
	elif (ret_code == 409):
		print("Error! The dongle detected has not been programmed by DinkeyAdd.")
	elif (ret_code == 410):
		print("Error! The dongle detected has a different Product Code to the one specified in DinkeyAdd.")
	elif (ret_code == 411):
		print("Error! The dongle detected does not contain the licence associated with this program.")
	elif (ret_code == 413):
		print("Error! This program has not been protected by DinkeyAdd. For guidance please read the DinkeyAdd chapter of the Dinkey manual.")
	elif (ret_code == 417):
		print("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.")
	elif (ret_code == 423):
		print("Error! The number of network users has been exceeded.")
	elif (ret_code == 435):
		print("Error! DinkeyServer has not been detected on the network.")
	elif (ret_code == 922):
		print("Error! The Software Key has expired.")
	else:
		print("An error occurred checking the dongle.\nError: " + str(ret_code) + ", Extended Error: " + str(extended_error))
	return
