Add Original SDK

This commit is contained in:
2026-05-06 07:47:36 +02:00
parent 2915e04380
commit 0ed627517a
674 changed files with 89334 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
Cython Sample Code
~~~~~~~~~~~~~~~~~~
NB - tested under Python 2.7 and Python 3.6 with Cython 0.29 with Visual Studio C compiler
but it should work for all versions of Python, Cython and C compiler. (We also tested the
32-bit code using MinGW).
If you are coding in Cython then you can link the Dinkey Pro/FD Runtime module directly to
your code (rather than having to call an external Python extension). See the notes for
Python sample code as to why this is preferable. Use:
dpwin32_coff.obj for 32-bit code
dpwin64.obj for 64-bit code
The source files in the project are:
File name Description
objtest.pyx main source code file for Cython containing protection functions
objtest.pxd implements the DRIS structure and function definitions
ddpro.h Used by the pxd file so we can call DDProtCheck (a hack because it is
difficult to declare a stdcall function in a pxd file)
setup.py python file to compile the Cython sample code
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.
NB - our sample code assumes that you are using 64-bit Python 3. If not then you will have
to make some small modifications to setup.py:
32-bit Python: modify extra_objects to list "dpwin32_coff.obj" instead of "dpwin64.obj"
Python 2: modify 'language_level' to be "2" (If you are using an old version of Python/Cython
you may have to modify the objtest.pyx code. e.g. "DEMO" instead of b'DEMO' and use
print instead of print()
To compile run the following command:
python setup.py build_ext --inplace --compiler=msvc
(for mingw32 use --compiler=mingw32)
So a makefile could look like this:
all:
python setup.py build_ext -c mingw32 --inplace
rm -rf ./build
rm -f *.c
Code marked with !!!! are places where you should customise the sample code so
that it will work properly:

View File

@@ -0,0 +1,2 @@
int __stdcall DDProtCheck(void*, unsigned char*);
int __stdcall DDGetNetUserList(char*, int*, void*, int, int*);

View File

@@ -0,0 +1,62 @@
#DRIS struture
cdef packed struct DRIS:
# the first 4 fields are never encrypted
char header[4] # should be set to DRIS
# inputs
int size # size of this structure
int seed1 # seed for data/dris encryption
int seed2 # as above
# (maybe encrypted from now on)
int function # specify only one function
int flags # options for the function selected. To use more than one OR them together: OPTION1 | OPTION2...
unsigned int execs_decrement # amount by which to dec execs if we use flag: DEC_MANY_EXECS
int data_crypt_key_num # number of the key (1-3) that the dongle uses to encrypt or decrypt user data
int rw_offset # offset in the dongle data area to read or write data
int rw_length # length of data are to read/write/encrypt/decrypt
char *rw_data_ptr # pointer to data to write / be read
char alt_prog_name[256] # protection check for different program instead of this one, must be null-terminated
int var_a # variable values for user algorithm
int var_b
int var_c
int var_d
int var_e
int var_f
int var_g
int var_h
int alg_number # the number of the user algorithm that you want to execute
# outputs
int ret_code # return code from the protection check
int ext_err # extended error
int type # type of dongle detected. 1 = Pro, 2 = FD
int model # model of dongle detected. 1 = Lite, 2 = Plus, 4 = Net 5, 7 = Net Unlimited
int sdsn # Software Developer's Serial Number
char prodcode[12] # Product Code (null-terminated)
unsigned int dongle_number
int update_number
unsigned int data_area_size # size of the data area used in the dongle detected
int max_alg_num # the maximum algorithm number in the dongle detected
int execs # executions left: -1 indicates 'no limit'
int exp_day # expiry day: -1 indicates 'no limit'
int exp_month # expiry month: -1 indicates 'no limit'
int exp_year # expiry year: -1 indicates 'no limit'
unsigned int features # features value
int net_users # maximum number of network users for the dongle detected: -1 indicates 'no limit'
int alg_answer # answer to the user algorithm executed with the given variable values
unsigned int fd_capacity # capacity of the data area in FD dongle.
char fd_drive[128] # drive letter of FD dongle detected e.g. 'f:\' (or mount name under Linux)
int swkey_type # 0 = no swkey detected, 1 = temporary software key, 2 = demo software key
int swkey_exp_day # software key expiry date (if software key detected)
int swkey_exp_month
int swkey_exp_year
#NU_INFO struture
cdef packed struct NU_INFO:
char licenceName[256]
char userName[50]
char computerName[256]
char ipAddress[16]
# declare DDProtCheck (we do it like this because it is stdcall - the default for Cython is cdecl)
cdef extern from "ddpro.h":
int __stdcall DDProtCheck(void*, unsigned char*)
int __stdcall DDGetNetUserList(char*, int*, void*, int, int*)

View File

@@ -0,0 +1,647 @@
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

View File

@@ -0,0 +1,16 @@
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
my_modules = [
Extension("objtest", ["objtest.pyx"], extra_objects=["dpwin64.obj", "advapi32.lib", "user32.lib"]),
]
for e in my_modules:
e.cython_directives = {'language_level': "3"} #all are Python-3
setup(
name = 'objtest',
cmdclass = {'build_ext': build_ext},
ext_modules = my_modules
)

View File

@@ -0,0 +1,155 @@
DllTest - sample code for Python
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This sample code will work with versions of Python 2.6 and higher.
Note - for Cython Sample code please look in the Cython sub-folder.
The source files in the project are:
File name Description
dlltest.py main source code file for Python containing protection functions
dris.py contains useful constants and functions that implement the DRIS structure.
The sample code works by calling a Python extension that communicates with the dongle.
The Python extension is located in the Modules/Python directory of the SDK. You need to
use the extension that matches the version of Python you are using. For each version of
Python we have extensions for each supported Operating System:
dpwin32py.pyd - Windows 32-bit
dpwin64py.pyd - Windows 64-bit
dplin32py.so - Linux 32-bit
dplin64py.so - Linux 64-bit
dpmac64py.so - MacOS 64-bit
Please contact Microcosm if you require a Python module for a different version to those we
provide and we will generate it for you.
The sample code (dris.py) will automatically work out the OS and bitness and load the
appropriate extension for you.
The sample code contains 10 functions which demonstrate some of the functionality offered by
the Dinkey Pro/FD system.
Code marked with !!!! are places where you should customise the sample code so
that it will work properly:
* Change the MY_SDSN value to the value of your SDSN
* Change the MY_PRODCODE value to the Product Code in the dongle
* Change the MyAlgorithm code (if you call a user algorithm)
* Change the CryptDRIS code (if you encrypt the DRIS)
* Change the CryptApiData (if you encrypt Data you send to our API)
* Change the MyRWAlgorithm code (if encrypt Data you send to our API using the R/W algorithm)
You should use DinkeyAdd to protect the Python extension(s) (e.g. dpmac64py.pyd etc...) and
not your Python code directly. Because your program is linked to the protected extension
then it is protected.
How To Protect The Python Sample Code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Use DinkeyAdd (using the API Method of protection) to protect the Dinkey Pro/FD Python
extensions we provide for the version of Python you want to support. (If you know which
Operating Systems you will support then you can just protect these extensions. If you are
not sure then just protect them all).
For example, if you were just distributing on Windows with 64-bit python 3.x then you should
specify the extension: Modules/Python/x86-amd64/3.x/dpwin64py.pyd as the input pathname and
the location of your python code as the output pathname. Do this for each extension you want
to protect. In our example you will have the following in your sample code folder:
dlltest.py
dris.py
dpwin64py.pyd [output from DinkeyAdd]
To test everything is working run the sample code with and without the dongle attached.
Note - the version of the extension you provide must match the version of Python that is
being run. If not you will get an error. In our experience this is different for each OS:
Windows: gives a sensible error message saying that the version of the extension is different.
Linux: We actually found that any 2.x extension could run on any 2.y Python (similar for 3.x).
Otherwise you get an error about PyInit_dpxxxx or init_dpxxx not being found in the
extension.
Mac: you get the very unhelpful error "PyThreadState_Get: no current thread"
How To Add Protection to your Python Code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1) Modify your python code to check the dongle - you can base your protection check function
on those from dlltest.py in the sample code. The sample code in dlltest.py 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 can implement the protection in a stronger way using the techniques described in
the "Increasing your Protection" chapter of the manual.
2) You should import dris.py into your python code.
3) You will probably want to modify some of the error messages in dris.py with your own.
However, you should still display or log any error codes returned by the protection check
otherwise you will not be able to identify the cause of the error. You can look up error
codes in our knowledge base: www.microcosm.com/kb.
4) Protect the relevant Python extensions just like in the step above for the sample code.
5) The protected Python extensions (for your version of Python) should be in the same
directory as the dris module.
6) Compile your Python code (py files) to pyc files for distribution (see instructions below).
Algorithms in Python
~~~~~~~~~~~~~~~~~~~~
Python implements modulo and integer division differently to other programming languages
like C, when it is applied to negative integers. Also, when you perform arithmetic in Python
it can expand the size of the variable to accommodate the size of the result. When the
algorithms are executed in the dongle they assume the output and all the inputs are signed
integers. To ensure that the algorithms are implemented in Python so that they match the
output of the dongle you need to be aware of the following:
* use the mod function defined in dlltest.py instead of the operator %
* use the div function defined in dlltest.py instead of the integer operator //
* apply the function dris.make_signed32bit to the value of the algorithm to ensure the output
is a 32-bit signed value
Level of Security
~~~~~~~~~~~~~~~~~
You should not distribute your py code as this editable and therefore not secure (an end
user could edit the file and remove all the protection checks). You should distribute the
compiled pyc or pyo files and the protected extensions. These files contain byte-code.
You can do this by compiling your python code like this:
python -m compile_all -b ./ [.\ for windows]
This will generate the pyc files in the same directory as the py files (rather than the
__pycache__ directory) and ensure that they will work in a pyc-only distribution.
However, although byte-code is not readable like source code, it is still possible to use
tools to decompile it back to source code. This will not be as easy to read as your source
code but a hacker might be able to work out what your code is doing, remove the protection
checks and re-compile. Also - it is possible to use tools that modify the byte-code directly.
One way around this is to write your code (or the most sensitive parts of your code) in Cython
(see Cython sample code) or in pure C (see C/C++ sample code). This is recommended if you have
the skills to do it. Another technique is to use a Python obfuscator so that the bytecode is
much more difficult to understand.
Another tool you could look at is Nuitka (nuitka.net) which converts Python code to C and then
compiles it.
Another possible hack is to use the Python interpreter to replace the protection check function
with a function that the hacker has written himself. You can combat this hack by using a user
algorithm or (not quite so good) reading data from the dongle data area. This works because it
means that the hacker would have to work out the algorithm (data) to defeat the protection.
Another solution is write some of your code in pure C and call it from Python.
Note - you may be tempted to not modify your source code and instead use py2exe and the Shell
Method to protect the resulting executable. However, this is not secure as the "compiled"
executable file is just a loader. Your compiled Python code is also included in the library.zip
file, so someone could just extract these files and use them. Therefore you should not use this
method but to use the API method with the source code instead. You can then use py2exe
(without the Shell method) if you prefer to distribute your software this way.
This is also the case for any other Python-to-executable software unless it specifically says
that your compiled Python code is encrypted. We have not seen this feature in any Python-to-exe
converter.

View File

@@ -0,0 +1,574 @@
import dris # code that communicates with the Dinkey Pro/FD runtime extension
import sys # so we can load the correct extension dependent on OS & bitness
# 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
MY_SDSN = 10101 # !!!! change this value to be the value of your SDSN (demo = 10101)
MY_PRODCODE = "DEMO" # !!!! change this value to be the value of the Product Code in the dongle
# ************************* our 10 functions **********************************
# ************************** ProtCheck ***************************************
def ProtCheck():
# create the DRIS and allocate the values we want to use
mydris = dris.create()
dris.set_function(mydris, dris.PROTECTION_CHECK) # standard protection check
dris.set_flags(mydris, 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(mydris)
if (ret_code != 0):
dris.DisplayError(ret_code, dris.get_ext_err(mydris))
return
# later in your code you can check other values in the DRIS...
if (dris.get_sdsn(mydris) != MY_SDSN):
print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
return
if (dris.get_prodcode(mydris) != 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
# later on in your program you can check the return code again
if (dris.get_ret_code(mydris) != 0):
print("Dinkey Dongle protection error")
return
print("It worked!")
return ret_code
# ************************** ProtCheckWithAlg ******************************
# this function should be used instead of % for modulo as python calculates this differently to C for negative numbers
def mod(x, y):
return abs(x)%abs(y)*(1,-1)[x<0]
# this function should be used instead of // for integer division as python calculates this differently to C for negative numbers
def div(x, y):
if (x >= 0) != (y >= 0) and x % y:
return x // y + 1
else:
return x // y
# !!!! 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 but use the mod function instead of % and div instead of //)
def MyAlgorithm(a, b, c, d, e, f, g, h):
value = a + b + c + d + e + f + g + h # place your algorithm here using mod instead of % and div instead of //
return dris.make_signed32bit(value) # this line always needs to be here to convert the value to a signed 32bit integer
# 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
def ProtCheckWithAlg():
# create the DRIS and allocate the values we want to use
mydris = dris.create()
dris.set_function(mydris, dris.EXECUTE_ALGORITHM) # standard protection check & execute algorithm
dris.set_flags(mydris, 0) # no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
dris.set_alg_number(mydris, 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.set_var_a(mydris, 1) # sample values
dris.set_var_b(mydris, 2)
dris.set_var_c(mydris, 3)
dris.set_var_d(mydris, 4)
dris.set_var_e(mydris, 5)
dris.set_var_f(mydris, 6)
dris.set_var_g(mydris, 7)
dris.set_var_h(mydris, 8)
ret_code = DDProtCheck(mydris)
if (ret_code != 0):
dris.DisplayError(ret_code, dris.get_ext_err(mydris))
return
# later in your code you can check other values in the DRIS...
if (dris.get_sdsn(mydris) != MY_SDSN):
print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
return
# later on in your program you can check the return code again
if (dris.get_ret_code(mydris) != 0):
print("Dinkey Dongle protection error")
return
# 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.get_var_a(mydris), dris.get_var_b(mydris), dris.get_var_c(mydris), dris.get_var_d(mydris), dris.get_var_e(mydris), \
dris.get_var_f(mydris), dris.get_var_g(mydris), dris.get_var_h(mydris)) != dris.get_alg_answer(mydris):
print("Dinkey protection error!\nYou have not patched your algorithm in the MyAlgorithm routine")
return
print("It worked!")
return ret_code
# ************************** 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.
def WriteBytes():
datatowrite = bytearray("Hello, World!", "ascii")
# create the DRIS and allocate the values we want to use
mydris = dris.create()
dris.set_function(mydris, dris.WRITE_DATA_AREA) # standard protection check and write data
dris.set_flags(mydris, dris.USE_FUNCTION_ARGUMENT) # we have to do it this way in Python
dris.set_rw_offset(mydris, 7)
dris.set_rw_length(mydris, len(datatowrite))
ret_code = DDProtCheck(mydris, datatowrite)
if (ret_code != 0):
dris.DisplayError(ret_code, dris.get_ext_err(mydris))
return
# later in your code you can check other values in the DRIS...
if (dris.get_sdsn(mydris) != MY_SDSN):
print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
return
# later on in your program you can check the return code again
if (dris.get_ret_code(mydris) != 0):
print("Dinkey Dongle protection error")
return
print("It worked!")
return ret_code
# ************************ 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
def ReadBytes():
datatoread = bytearray(13)
# create the DRIS and allocate the values we want to use
mydris = dris.create()
dris.set_function(mydris, dris.READ_DATA_AREA) # standard protection check and read data
dris.set_flags(mydris, dris.USE_FUNCTION_ARGUMENT) # we have to do it this way in Python
dris.set_rw_offset(mydris, 7)
dris.set_rw_length(mydris, len(datatoread))
ret_code = DDProtCheck(mydris, datatoread)
if (ret_code != 0):
dris.DisplayError(ret_code, dris.get_ext_err(mydris))
return
# later in your code you can check other values in the DRIS...
if (dris.get_sdsn(mydris) != MY_SDSN):
print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
return
# later on in your program you can check the return code again
if (dris.get_ret_code(mydris) != 0):
print("Dinkey Dongle protection error")
return
print("It worked! Dinkey Dongle Data area, offset 7 is " + datatoread.decode("ascii"))
return ret_code
# ************************** 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
def EncryptUserData():
data = bytearray("Hello, World!", "ascii")
# create the DRIS and allocate the values we want to use
mydris = dris.create()
dris.set_function(mydris, dris.ENCRYPT_USER_DATA) # standard protection check and encrypt data
dris.set_flags(mydris, dris.USE_FUNCTION_ARGUMENT) # we have to do it this way in Python
dris.set_data_crypt_key_num(mydris, 1)
dris.set_rw_length(mydris, len(data))
ret_code = DDProtCheck(mydris, data)
if (ret_code != 0):
dris.DisplayError(ret_code, dris.get_ext_err(mydris))
return
# ... 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
mydris2 = dris.create()
dris.set_function(mydris2, dris.DECRYPT_USER_DATA) # standard protection check and decrypt data
dris.set_flags(mydris2, dris.USE_FUNCTION_ARGUMENT) # we have to do it this way in Python
dris.set_data_crypt_key_num(mydris2, 1)
dris.set_rw_length(mydris2, len(data))
ret_code = DDProtCheck(mydris2, data)
if (ret_code != 0):
dris.DisplayError(ret_code, dris.get_ext_err(mydris2))
return
# later in your code you can check other values in the DRIS...
if (dris.get_sdsn(mydris2) != MY_SDSN):
print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
return
# later on in your program you can check the return code again
if (dris.get_ret_code(mydris2) != 0):
print("Dinkey Dongle protection error")
return
print("It worked! Data decrypted is " + data.decode("ascii"))
return ret_code
# !!!!!!!!!!! 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
def MyRWAlgorithm(a, b, c, d, e, f, g, h):
value = a ^ b ^ c ^ d ^ e ^ f
return dris.make_signed32bit(value)
# !!!! please overwrite this function with the one generated by DinkeyAdd
# It is not generated by DinkeyAdd for Python currently so just modify parameters 1,2 and 3 accordingly
def CryptDRIS(mydris):
bigseed = bytearray(256)
S = bytearray(256)
for i in range(0, 256, 8):
bigseed[i:i+4] = mydris[8:12] # seed1
bigseed[i+4:i+8] = mydris[12:16] # seed2
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,dris.DRIS_SIZE):
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
mydris[k] = mydris[k] ^ S[t]
return
# !!!! please overwrite this function with the one generated by DinkeyAdd
# It is not generated by DinkeyAdd for Python currently. This is set-up for using r/w parameters
def CryptApiData(data, mydris, length, alg_answer):
bigseed = bytearray(256)
S = bytearray(256)
for i in range(0, 256, 8):
bigseed[i:i+4] = mydris[8:12] # seed1
bigseed[i+4:i+8] = mydris[12:16] # seed2
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 # parameter 1
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
data[k] = data[k] ^ S[t]
return
# ************************* ProtCheckEnc *******************************
def ProtCheckEnc():
# We encrypt the DRIS passed to the API. Patch the CryptDRIS function in this file from source code generated by DinkeyAdd.
mydris = dris.create()
dris.set_function(mydris, dris.PROTECTION_CHECK) # standard protection check
dris.set_flags(mydris, 0) # no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
CryptDRIS(mydris) # encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)
ret_code = DDProtCheck(mydris)
CryptDRIS(mydris) # decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)
if (ret_code != 0):
dris.DisplayError(ret_code, dris.get_ext_err(mydris))
return
# later in your code you can check other values in the DRIS...
if (dris.get_sdsn(mydris) != MY_SDSN):
print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
return
if (dris.get_prodcode(mydris) != 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
# later on in your program you can check the return code again
if (dris.get_ret_code(mydris) != 0):
print("Dinkey Dongle protection error")
return
print("It worked!")
return ret_code
# ************************ 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.
def ProtCheckWithAlgEnc():
# create the DRIS and allocate the values we want to use
mydris = dris.create()
dris.set_function(mydris, dris.EXECUTE_ALGORITHM) # standard protection check & execute algorithm
dris.set_flags(mydris, 0) # no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
dris.set_alg_number(mydris, 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.set_var_a(mydris, 1) # sample values
dris.set_var_b(mydris, 2)
dris.set_var_c(mydris, 3)
dris.set_var_d(mydris, 4)
dris.set_var_e(mydris, 5)
dris.set_var_f(mydris, 6)
dris.set_var_g(mydris, 7)
dris.set_var_h(mydris, 8)
CryptDRIS(mydris) # encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)
ret_code = DDProtCheck(mydris)
CryptDRIS(mydris) # decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)
if (ret_code != 0):
dris.DisplayError(ret_code, dris.get_ext_err(mydris))
return
# later in your code you can check other values in the DRIS...
if (dris.get_sdsn(mydris) != MY_SDSN):
print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
return
# later on in your program you can check the return code again
if (dris.get_ret_code(mydris) != 0):
print("Dinkey Dongle protection error")
return
# 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.get_var_a(mydris), dris.get_var_b(mydris), dris.get_var_c(mydris), dris.get_var_d(mydris), dris.get_var_e(mydris), \
dris.get_var_f(mydris), dris.get_var_g(mydris), dris.get_var_h(mydris)) != dris.get_alg_answer(mydris):
print("Dinkey protection error!\nYou have not patched your algorithm in the MyAlgorithm routine")
return
print("It worked!")
return ret_code
# ************************** 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 from source code generated by DinkeyAdd.
def WriteBytesEnc():
datatowrite = bytearray("Hello, World!", "ascii")
# create the DRIS and allocate the values we want to use
mydris = dris.create()
dris.set_function(mydris, dris.WRITE_DATA_AREA) # standard protection check and write data
dris.set_flags(mydris, dris.USE_FUNCTION_ARGUMENT) # we have to do it this way in Python
dris.set_rw_offset(mydris, 7)
dris.set_rw_length(mydris, len(datatowrite))
# calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm
alg_ans = MyRWAlgorithm(dris.get_var_a(mydris), dris.get_var_b(mydris), dris.get_var_c(mydris), dris.get_var_d(mydris), \
dris.get_var_e(mydris), dris.get_var_f(mydris), dris.get_var_g(mydris), dris.get_var_h(mydris))
# encrypt data we want to write.
CryptApiData(datatowrite, mydris, len(datatowrite), alg_ans)
CryptDRIS(mydris) # encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)
ret_code = DDProtCheck(mydris, datatowrite)
CryptDRIS(mydris) # decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)
if (ret_code != 0):
dris.DisplayError(ret_code, dris.get_ext_err(mydris))
return
# later in your code you can check other values in the DRIS...
if (dris.get_sdsn(mydris) != MY_SDSN):
print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
return
# later on in your program you can check the return code again
if (dris.get_ret_code(mydris) != 0):
print("Dinkey Dongle protection error")
return
print("It worked!")
return ret_code
# ************************ 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 from the API. Patch the CryptDRIS and CryptApiData functions in this file from source code generated by DinkeyAdd.
def ReadBytesEnc():
datatoread = bytearray(13)
# create the DRIS and allocate the values we want to use
mydris = dris.create()
dris.set_function(mydris, dris.READ_DATA_AREA) # standard protection check and read data
dris.set_flags(mydris, dris.USE_FUNCTION_ARGUMENT) # we have to do it this way in Python
dris.set_rw_offset(mydris, 7)
dris.set_rw_length(mydris, len(datatoread))
# calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm
alg_ans = MyRWAlgorithm(dris.get_var_a(mydris), dris.get_var_b(mydris), dris.get_var_c(mydris), dris.get_var_d(mydris), \
dris.get_var_e(mydris), dris.get_var_f(mydris), dris.get_var_g(mydris), dris.get_var_h(mydris))
CryptDRIS(mydris) # encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)
ret_code = DDProtCheck(mydris, datatoread)
CryptDRIS(mydris) # decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)
if (ret_code != 0):
dris.DisplayError(ret_code, dris.get_ext_err(mydris))
return
# later in your code you can check other values in the DRIS...
if (dris.get_sdsn(mydris) != MY_SDSN):
print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
return
# later on in your program you can check the return code again
if (dris.get_ret_code(mydris) != 0):
print("Dinkey Dongle protection error")
return
# decrypt data that was read
CryptApiData(datatoread, mydris, len(datatoread), alg_ans)
print("It worked! Dinkey Dongle Data area, offset 7 is " + datatoread.decode("ascii"))
return ret_code
# ************************** 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 to/from the API. Patch the CryptDRIS and CryptApiData functions in this file from source code generated by DinkeyAdd.
def EncryptUserDataEnc():
data = bytearray("Hello, World!", "ascii")
# create the DRIS and allocate the values we want to use
mydris = dris.create()
dris.set_function(mydris, dris.ENCRYPT_USER_DATA) # standard protection check and encrypt data
dris.set_flags(mydris, dris.USE_FUNCTION_ARGUMENT) # we have to do it this way in Python
dris.set_data_crypt_key_num(mydris, 1)
dris.set_rw_length(mydris, len(data))
# calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm
alg_ans = MyRWAlgorithm(dris.get_var_a(mydris), dris.get_var_b(mydris), dris.get_var_c(mydris), dris.get_var_d(mydris), \
dris.get_var_e(mydris), dris.get_var_f(mydris), dris.get_var_g(mydris), dris.get_var_h(mydris))
# encrypt data we want to pass.
CryptApiData(data, mydris, len(data), alg_ans)
CryptDRIS(mydris) # encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)
ret_code = DDProtCheck(mydris, data)
CryptDRIS(mydris) # decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)
if (ret_code != 0):
dris.DisplayError(ret_code, dris.get_ext_err(mydris))
return
# ... 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
mydris2 = dris.create()
dris.set_function(mydris2, dris.DECRYPT_USER_DATA) # standard protection check and decrypt data
dris.set_flags(mydris2, dris.USE_FUNCTION_ARGUMENT) # we have to do it this way in Python
dris.set_data_crypt_key_num(mydris2, 1)
dris.set_rw_length(mydris2, len(data))
CryptDRIS(mydris2) # encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)
ret_code = DDProtCheck(mydris2, data)
CryptDRIS(mydris2) # decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)
if (ret_code != 0):
dris.DisplayError(ret_code, dris.get_ext_err(mydris2))
return
# later in your code you can check other values in the DRIS...
if (dris.get_sdsn(mydris2) != MY_SDSN):
print("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.")
return
# later on in your program you can check the return code again
if (dris.get_ret_code(mydris2) != 0):
print("Dinkey Dongle protection error")
return
alg_ans = MyRWAlgorithm(dris.get_var_a(mydris2), dris.get_var_b(mydris2), dris.get_var_c(mydris2), dris.get_var_d(mydris2), \
dris.get_var_e(mydris2), dris.get_var_f(mydris2), dris.get_var_g(mydris2), dris.get_var_h(mydris2))
# decrypt data passed to us by the API
CryptApiData(data, mydris2, len(data), alg_ans)
print("It worked! Data decrypted is " + data.decode("ascii"))
return ret_code
# entry point
if __name__ == "__main__":
# load the correct extension dependent on the OS and bitness of Python
if sys.platform == 'win32':
if sys.maxsize > 2**32:
from dpwin64py import DDProtCheck
else:
from dpwin32py import DDProtCheck
elif 'linux' in sys.platform:
if sys.maxsize > 2**32:
from dplin64py import DDProtCheck
else:
from dplin32py import DDProtCheck
elif sys.platform == 'darwin':
if sys.maxsize > 2**32:
from dpmac64py import DDProtCheck
else:
from dpmac32py import DDProtCheck
# choose the protection check function of your choice here. We have chosen the basic protection check.
ProtCheck()

View File

@@ -0,0 +1,254 @@
import random
import sys
#define constants
#these constants depend on the bitness of the OS - the DRIS is 4 bytes bigger for 64-bit code
if sys.maxsize > 2**32:
DRIS_SIZE = 564 # DRIS size is bigger for 64-bit (it has an 8-byte pointer)
python64 = 4 # so we can adjust structure offsets for 64-bit python
else:
DRIS_SIZE = 560
python64 = 0
#function values
PROTECTION_CHECK = 1 # checks for dongle, check program params...
EXECUTE_ALGORITHM = 2 # protection check + calculate answer for specified algorithm with specified inputs
WRITE_DATA_AREA = 3 # protection check + writes dongle data area
READ_DATA_AREA = 4 # protection check + reads dongle data area
ENCRYPT_USER_DATA = 5 # protection check + the dongle will encrypt user data
DECRYPT_USER_DATA = 6 # protection check + the dongle will decrypt user data
FAST_PRESENCE_CHECK = 7 # checks for the presence of the correct dongle only with minimal security, no flags allowed.
STOP_NET_USER = 8 # stops a network user (a protection check is NOT performed)
# flags - can specify as many as you like
DEC_ONE_EXEC = 1 # decrement execs by 1
DEC_MANY_EXECS = 2 # decrement execs by number specified in execs_decrement
START_NET_USER = 4 # starts a network user
USE_FUNCTION_ARGUMENT = 16 # use the extra argument in the function for pointers
CHECK_LOCAL_FIRST = 32 # always look in local ports before looking in network ports
CHECK_NETWORK_FIRST = 64 # always look on the network before looking in local ports
USE_ALT_LICENCE_NAME = 128 # use name specified in alt_licence_name instead of the default one
DONT_SET_MAXDAYS_EXPIRY = 256 # if the max days expiry date has not been calculated then do not do it this time
MATCH_DONGLE_NUMBER = 512 # restrict the search to match the dongle number specified in the DRIS
DONT_RETURN_FD_DRIVE = 1024 # if an FD dongle has been detected then don't return the flash drive/mount name
# set 4 bytes in our DRIS byte array from an integer
def set4bytes(data, offset, value):
data[offset] = value & 0xff
data[offset+1] = (value >> 8) & 0xff
data[offset+2] = (value >> 16) & 0xff
data[offset+3] = (value >> 24) & 0xff
return
# trunctates a value to 32-bit and converts to a signed integer
def make_signed32bit(value):
value = value & 0xffffffff
if (value > (2**31)):
value = value - (2**32)
return value
# get 4 bytes in our DRIS byte array and convert to an unsigned integer
# this returns an unsigned value, which is useful for dongle number, features, etc...
def get4bytes(data, offset):
return (data[offset] + data[offset+1]*0x100 + data[offset+2]*0x10000 + data[offset+3]*0x1000000)
# this returns a signed value, which is useful for alg_answer, expiry day...
def get4bytes_signed(data, offset):
value = data[offset] + data[offset+1]*0x100 + data[offset+2]*0x10000 + data[offset+3]*0x1000000
if (value > (2**31)):
value = value - (2**32)
return value
# create the DRIS structure as a byte array, populate it with random values, initialise the header and size fields
def create():
dris = bytearray(DRIS_SIZE)
random.seed()
for n in range(DRIS_SIZE):
dris[n] = random.randint(0, 255)
dris[0] = ord('D')
dris[1] = ord('R')
dris[2] = ord('I')
dris[3] = ord('S')
set4bytes(dris, 4, DRIS_SIZE)
return dris
# these functions set various values in the DRIS
def set_function(dris, value):
set4bytes(dris, 16, value)
def set_flags(dris, value):
set4bytes(dris, 20, value)
def set_execs_decrement(dris, value):
set4bytes(dris, 24, value)
def set_data_crypt_key_num(dris, value):
set4bytes(dris, 28, value)
def set_rw_offset(dris, value):
set4bytes(dris, 32, value)
def set_rw_length(dris, value):
set4bytes(dris, 36, value)
def set_alt_licence_name(dris, licence_name):
dris[44+python64:44+python64+len(licence_name)] = licence_name
dris[44+python64+len(licence_name)] = 0 # null-terminate the string
def set_var_a(dris, value):
set4bytes(dris, 300+python64, value)
def set_var_b(dris, value):
set4bytes(dris, 304+python64, value)
def set_var_c(dris, value):
set4bytes(dris, 308+python64, value)
def set_var_d(dris, value):
set4bytes(dris, 312+python64, value)
def set_var_e(dris, value):
set4bytes(dris, 316+python64, value)
def set_var_f(dris, value):
set4bytes(dris, 320+python64, value)
def set_var_g(dris, value):
set4bytes(dris, 324+python64, value)
def set_var_h(dris, value):
set4bytes(dris, 328+python64, value)
def set_alg_number(dris, value):
set4bytes(dris, 332+python64, value)
# these functions get values from the DRIS
def get_ret_code(dris):
return get4bytes(dris, 336+python64)
def get_ext_err(dris):
return get4bytes(dris, 340+python64)
def get_type(dris):
return get4bytes(dris, 344+python64)
def get_model(dris):
return get4bytes(dris, 348+python64)
def get_sdsn(dris):
return get4bytes(dris, 352+python64)
def get_prodcode(dris):
for n in range(356+python64,368+python64):
if dris[n] == 0:
break
return dris[356+python64:n].decode("utf-8")
def get_dongle_number(dris):
return get4bytes(dris, 368+python64)
def get_update_number(dris):
return get4bytes(dris, 372+python64)
def get_data_area_size(dris):
return get4bytes(dris, 376+python64)
def get_max_alg_num(dris):
return get4bytes(dris, 380+python64)
def get_execs(dris):
return get4bytes(dris, 384+python64)
def get_exp_day(dris):
return get4bytes_signed(dris, 388+python64)
def get_exp_month(dris):
return get4bytes_signed(dris, 392+python64)
def get_exp_year(dris):
return get4bytes_signed(dris, 396+python64)
def get_features(dris):
return get4bytes(dris, 400+python64)
def get_net_users(dris):
return get4bytes(dris, 404+python64)
def get_alg_answer(dris):
return get4bytes_signed(dris, 408+python64)
def get_fd_capacity(dris):
return get4bytes(dris, 412+python64)
def get_fd_drive(dris):
for n in range(416+python64,544+python64):
if dris[n] == 0:
break
return dris[416+python64:n].decode("utf-8")
def get_swkey_type(dris):
return get4bytes(dris, 544+python64)
def get_swkey_exp_day(dris):
return get4bytes(dris, 548+python64)
def get_swkey_exp_month(dris):
return get4bytes(dris, 552+python64)
def get_swkey_exp_year(dris):
return get4bytes(dris, 556+python64)
# you may also want to retrieve var_a values that you already entered into the dris!
def get_var_a(dris):
return get4bytes_signed(dris, 300+python64)
def get_var_b(dris):
return get4bytes_signed(dris, 304+python64)
def get_var_c(dris):
return get4bytes_signed(dris, 308+python64)
def get_var_d(dris):
return get4bytes_signed(dris, 312+python64)
def get_var_e(dris):
return get4bytes_signed(dris, 316+python64)
def get_var_f(dris):
return get4bytes_signed(dris, 320+python64)
def get_var_g(dris):
return get4bytes_signed(dris, 324+python64)
def get_var_h(dris):
return get4bytes_signed(dris, 328+python64)
# function to display the most common error codes.
# You will want to change this with your own error messages
def DisplayError(ret_code, extended_error):
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