unit apitest;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, System.UITypes, { !!!! for Delphi XE and earlier remove System.UITypes }
  dris;                              { contains the DRIS structure declaration }

type
  TForm1 = class(TForm)
    Label1: TLabel;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ !! We strongly recommend that you read the "readme.txt" file in the sample code folder.
 !! It contains important instructions on how to use the sample code }

{ The sample code contains 11 functions. You will not need to use all these
functions in your code. Just use which ever functions are most appropriate
and customise in your own way. The sample code is just a guide. You should
implement the protection in a stronger way using the techniques described in
the "Increasing your Protection" chapter of the manual. To choose which function
this code uses see the startup code at the bottom of this file.

this file contains 11 protection-check functions.
 longint ProtCheck();	 			a standard protection check
 longint ProtCheckWithAlg();			a protection check & executing an Algorithm
 longint WriteBytes();				a protection check & write data to the dongle
 longint ReadBytes();				a protection check & read data from the dongle
 longint 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
 longint ProtCheckEnc();				a standard protection check
 longint ProtCheckWithAlgEnc();			a protection check & executing an Algorithm
 longint WriteBytesEnc();			a protection check & write data to the dongle
 longint ReadBytesEnc();				a protection check & read data from the dongle
 longint EncryptUserDataEnc();			a protection check & encrypting data and then decrypting the data

 longint DisplayNetUsers(net_users, model);    this function displays the current network users 
 
 If you are using Dinkey Lite then you can only use 4 functions:
ProtCheck, ProtCheckWithAlg, ProtCheckEnc, ProtCheckWithAlgEnc
 }

const MY_SDSN = 10101;                          { !!!! change this value to be the value of your SDSN (demo = 10101) }
const MY_PRODCODE = 'DEMO';
 
{ this procedure initialises the DRIS with random bytes }
procedure random_set(data: PByte; length: integer);
var
    i : integer;
begin
    Randomize();
    for i := 0 to length-1 do
    begin
        data^ := Random(256);
        Inc(data);                      { increment the pointer so we can access the next byte }
    end;
end;

{ displays messages for the most common errors. You will want to change this for your code }
procedure DisplayError(ret_code, extended_error:integer);
var
    display : string;
begin
    case ret_code of
        401: display := 'Error! No dongles detected!';
        403: display := 'Error! The dongle detected has a different type to the one specified in DinkeyAdd.';
        404: display := 'Error! The dongle detected has a different model to those specified in DinkeyAdd.';
        409: display := 'Error! The dongle detected has not been programmed by DinkeyAdd.';
        410: display := 'Error! The dongle detected has a different Product Code to the one specified in DinkeyAdd.';
        411: display := 'Error! The dongle detected does not contain the licence associated with this program.';
        413: display := 'Error! This program has not been protected by DinkeyAdd. For guidance please read the DinkeyAdd chapter of the Dinkey manual.';
        417: display := '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.';
        423: display := 'Error! The number of network users has been exceeded.';
        435: display := 'Error! DinkeyServer has not been detected on the network.';
        922: display := 'Error! The Software Key has expired.';
    else
        display := 'An error occurred checking the dongle. Error: ' + IntToStr(ret_code) + '. Extended Error code: ' + IntToStr(extended_error);
    end;
    messagedlg(display, mtError, [mbok], 0);
end;

{ *************************  our 11 functions follow *************************** }

{ ***********************   DisplayNetUsers   ******************************* }
{ This function displays the current network users (for DinkeyNet only).
  The DDProtCheck function must be called before this function is called.
  (Normally you would use DinkeyServer to view the network users but this enables you to do it from the client if you want) }
Function DisplayNetUsers (net_users, model : LongInt) : LongInt;
var
    display : string;
    nu_info: packed array[0..99] of nu_infoType;  	     { !!!! this assumes that you will display max. 100 net users }
    i, max_net_users, num_net_users, extended_error : LongInt;

begin
    if (net_users = -1) then
    begin
    	max_net_users := 100;		   { if unlimited network users are allowed then display up to 100 users (for example) If you want more than 1000 net users you need to change the value above}
    end
    else
    begin
        max_net_users := net_users;
    end;

    if (net_users = 0) then				     { no network users are allowed, so nothing to do! }
    begin
        result := 0;
        exit;
    end;

    if (model < 3) then				 
    begin
        messagedlg('A network dongle has not been detected. Therefore you cannot display the network users.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { now get net users }
    result := DDGetNetUserList(NIL, @num_net_users, @nu_info, max_net_users, @extended_error);

    if (result <> 0) then
    begin
        messagedlg('Error ' + IntToStr(result) + '/' + IntToStr(extended_error) + ' finding network user information.', mtError, [mbok], 0);
    end
    else
    begin
        display:= 'number of net users: ' + IntToStr(num_net_users) + #13#10;
        for i := 0 to num_net_users-1 do
        begin
            display := display + IntToStr(i+1) + '. licence: ' + String(nu_info[i].licenceName) + ', user: ' + String(nu_info[i].userName) +
                        ', computer: ' + String(nu_info[i].computerName) + ', ipaddress: ' + String(nu_info[i].ipAddress) + #13#10;
        end;
        messagedlg(display, mtInformation, [mbok], 0);
    end;
end;

{ **************************  ProtCheck  *************************************** }
Function ProtCheck : LongInt;
var
    dris: drisType;

begin
    { set the DRIS to have random values }
    random_set(@dris, SizeOf(dris));
    { then set the values we want to use }
    Move(AnsiString('DRIS'), dris.header, 4);
    dris.size:= SizeOf(dris);
    dris.myfunction:= 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,... }

    { call dongle-checking code }
    result := DDProtCheck(@dris, NIL);

    if (result <> 0) then
    begin
        DisplayError(result, dris.ext_err);	 { display messages for any errors that have occurred }
        exit;
    end;

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

    if (dris.sdsn <> MY_SDSN) then
    begin
        messagedlg('Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    if (dris.prodcode <> AnsiString(MY_PRODCODE)) then
    begin
        messagedlg('Incorrect Product Code! Please modify your source code so that MY_PRODCODE is set to be the Product Code in the dongle.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { later on in your program you can check the return code again }
    if (dris.ret_code <> 0) then
    begin
        messagedlg('Dinkey Dongle protection error.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;
	
    { if you are using a network dongle and you want to list the network users then use this function: }
    { DisplayNetUsers(dris.net_users, dris.model); }
end;

{ **************************  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).
If you are using Dinkey Lite the copy the source code from DinkeyLook  }
Function MyAlgorithm(a,b,c,d,e,f,g,h:LongInt):LongInt;
begin
   MyAlgorithm:= a + b + c + d + e + f + g + h;
end;

{ 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 Dinkey Lite copy source code from DinkeyLook}
Function ProtCheckWithAlg : LongInt;
var
    dris: drisType;

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

    { call dongle-checking code }
    result := DDProtCheck(@dris, NIL);

    if (result <> 0) then
    begin
        DisplayError(result, dris.ext_err);	 { display messages for any errors that have occurred }
        exit;
    end;

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

    if (dris.sdsn <> MY_SDSN) then
    begin
        messagedlg('Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { later on in your program you can check the return code again }
    if (dris.ret_code <> 0) then
    begin
        messagedlg('Dinkey Dongle protection error.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { 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) then
    begin
        messagedlg('Dinkey protection error! You have not patched your algorithm in the MyAlgorithm routine', mtError, [mbok], 0);
        result := -1;
        exit;
    end;
end;

{ **************************  WriteBytes  *************************************** }
{ This writes the Data "Hello, World" to the dongle data area at offset 7
  In order for this function to work you will need to have a data area in your dongle that is at least 21 bytes long. }

Function WriteBytes : LongInt;
var
    dris: drisType;
    DataToWrite : array[0..13] of AnsiChar;

begin
    { set the DRIS to have random values }
    random_set(@dris, SizeOf(dris));
    { then set the values we want to use }
    Move(AnsiString('DRIS'), dris.header, 4);
    dris.size:= SizeOf(dris);
    dris.myfunction:= WRITE_DATA_AREA;           { standard protection check & write data}
    dris.flags:= 0;                              { no extra flags, but you may want to specify some if you want to start a network user or decrement execs,... }
    dris.rw_offset := 7;
    dris.rw_length := 13;
    DataToWrite := 'Hello, World!';              { string to write }
    dris.rw_data_ptr := Addr(DataToWrite);

    { call dongle-checking code }
    result := DDProtCheck(@dris, NIL);

    if (result <> 0) then
    begin
        DisplayError(result, dris.ext_err);	 { display messages for any errors that have occurred }
        exit;
    end;

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

    if (dris.sdsn <> MY_SDSN) then
    begin
        messagedlg('Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { later on in your program you can check the return code again }
    if (dris.ret_code <> 0) then
    begin
        messagedlg('Dinkey Dongle protection error.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;
end;

{ **************************  ReadBytes  *************************************** }
{ This reads 14 bytes of data from offset 7 in the dongle data area
  In order for this function to work you will need to have a data area in your dongle that is at least 21 bytes long.
  In order for the data to be displayed properly by this routine it will need to be text data }
Function ReadBytes : LongInt;
var
    dris: drisType;
    DataToRead : array[0..13] of AnsiChar;
    display : string;

begin
    { set the DRIS to have random values }
    random_set(@dris, SizeOf(dris));
    { then set the values we want to use }
    Move(AnsiString('DRIS'), dris.header, 4);
    dris.size:= SizeOf(dris);
    dris.myfunction:= READ_DATA_AREA;            { standard protection check & write data}
    dris.flags:= 0;                              { no extra flags, but you may want to specify some if you want to start a network user or decrement execs,... }
    dris.rw_offset := 7;
    dris.rw_length := 13;                        { we will add null termination after reading }
    dris.rw_data_ptr := Addr(DataToRead);

    { call dongle-checking code }
    result := DDProtCheck(@dris, NIL);

    if (result <> 0) then
    begin
        DisplayError(result, dris.ext_err);	 { display messages for any errors that have occurred }
        exit;
    end;

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

    if (dris.sdsn <> MY_SDSN) then
    begin
        messagedlg('Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { later on in your program you can check the return code again }
    if (dris.ret_code <> 0) then
    begin
        messagedlg('Dinkey Dongle protection error.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

   DataToRead[13] := Chr(0);                      { null end of string }
   display := 'Dinkey Dongle data area contains: ' + DataToRead;
   messagedlg(display, mtInformation, [mbok], 0);
end;

{ **************************  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 }
Function EncryptUserData : LongInt;
var
    dris: drisType;
    display : string;
    DataString : array[0..13] of AnsiChar;

begin
    { set the DRIS to have random values }
    random_set(@dris, SizeOf(dris));
    { then set the values we want to use }
    Move(AnsiString('DRIS'), dris.header, 4);
    dris.size:= SizeOf(dris);
    dris.myfunction:= ENCRYPT_USER_DATA;         { standard protection check & 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 := 13;
    DataString := 'Hello, World!';               { string to encrypt }
    dris.rw_data_ptr := Addr(DataString);

    { call dongle-checking code }
    result := DDProtCheck(@dris, NIL);

    if (result <> 0) then
    begin
        DisplayError(result, dris.ext_err);	 { display messages for any errors that have occurred }
        exit;
    end;

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

    { Now decrypt the same data to get the original values }
    random_set(@dris, SizeOf(dris));
    { then set the values we want to use }
    Move(AnsiString('DRIS'), dris.header, 4);
    dris.size:= SizeOf(dris);
    dris.myfunction:= DECRYPT_USER_DATA;         { standard protection check & 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 := 13;
    dris.rw_data_ptr := Addr(DataString);

    { call dongle-checking code }
    result := DDProtCheck(@dris, NIL);

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

    if (dris.sdsn <> MY_SDSN) then
    begin
        messagedlg('Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { later on in your program you can check the return code again }
    if (dris.ret_code <> 0) then
    begin
        messagedlg('Dinkey Dongle protection error.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

   DataString[13] := Chr(0);                      { null end of string }
   display := 'Decrypted data is: ' + DataString;
   messagedlg(display, mtInformation, [mbok], 0);
end;

{!!!!   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 }
Function MyRWAlgorithm(a,b,c,d,e,f,g,h:LongInt):LongInt;
begin
   MyRWAlgorithm:= a xor b xor c xor d xor e xor f;
end;

{ !!!! please overwrite this function with the one generated by DinkeyAdd }
procedure CryptDRIS(dris : PByte; seed1,seed2 : Longword);
var
	i,j,k : Integer;
	bigseed,S : packed array[0..255] of byte;
	temp,t : byte;
begin
	i := 0;
	while (i < 255) do
	begin
		Move(seed1, bigseed[i], 4);			{NB move actually copies data}
		Move(seed2, bigseed[i+4], 4);
		i := i + 8;
	end;

	for i := 0 to 255 do S[i]:= i;

	j := 0;
	for i := 0 to 255 do
	begin
		j := (j + S[i] + bigseed[i] + 123) Mod 256;
		temp := S[i];
		S[i] := S[j];
		S[j] := temp;
	end;
	i := 0;
	j := 0;
	Inc(dris, 16);			{the first part of the DRIS is not encrypted}
        for k := 16 to SizeOf(drisType)-1 do
	begin
		i := (i + 1) Mod 256;
		j := (j + S[i] + 212) Mod 256;
		temp := S[i];
		S[i] := S[j];
		S[j] := temp;
		t := (S[i] + S[j] + 97) Mod 256;
		dris^ := dris^ xor S[t];
		Inc(dris);
	end;
end;
{ !!!! please overwrite this function with the one generated by DinkeyAdd }
procedure CryptApiData(data : pbyte; length : integer; seed1,seed2 : Longword; alg_answer: LongInt);
var
	i,j,k : Integer;
	bigseed,S : packed array[0..255] of byte;
	temp,t : byte;
begin
	i := 0;
	while (i < 255) do
	begin
		Move(seed1, bigseed[i], 4);			{NB move actually copies data}
		Move(seed2, bigseed[i+4], 4);
		i := i + 8;
	end;

	for i := 0 to 255 do S[i]:= i;

	j := 0;
	for i := 0 to 255 do
	begin
		j := (j + S[i] + bigseed[i] + (alg_answer And $FF)) Mod 256;
		temp := S[i];
		S[i] := S[j];
		S[j] := temp;
	end;
	i := 0;
	j := 0;
	for k := 0 to length-1 do
	begin
		i := (i + 1) Mod 256;
		j := (j + S[i] + ((alg_answer shr 8) And $FF)) Mod 256;
		temp := S[i];
		S[i] := S[j];
		S[j] := temp;
		t := (S[i] + S[j] + ((alg_answer shr 16) And $FF)) Mod 256;
		data^ := data^ xor S[t];
		Inc(data);
	end;
end;

{ **************************  ProtCheckEnc  ************************************ }
{ We encrypt the DRIS passed to the API.
 Patch the CryptDRIS function in this file from source code generated by DinkeyAdd. }
Function ProtCheckEnc : LongInt;
var
    dris: drisType;

begin
    { set the DRIS to have random values }
    random_set(@dris, SizeOf(dris));             { NB this also fills seed1, seed2 with random values }
    { then set the values we want to use }
    Move(AnsiString('DRIS'), dris.header, 4);
    dris.size:= SizeOf(dris);
    dris.myfunction:= 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 }

    result := DDProtCheck(@dris, NIL);

    CryptDRIS(@dris, dris.seed1, dris.seed2);   { decrypt DRIS - !!!! you should separate from DDProtCheck for greater security }

    if (result <> 0) then
    begin
        DisplayError(result, dris.ext_err);	 { display messages for any errors that have occurred }
        exit;
    end;

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

    if (dris.sdsn <> MY_SDSN) then
    begin
        messagedlg('Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    if (dris.prodcode <> AnsiString(MY_PRODCODE)) then
    begin
        messagedlg('Incorrect Product Code! Please modify your source code so that MY_PRODCODE is set to be the Product Code in the dongle.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { later on in your program you can check the return code again }
    if (dris.ret_code <> 0) then
    begin
        messagedlg('Dinkey Dongle protection error.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;
end;

{ **************************  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 Dinkey Lite copy source code from DinkeyLook.
  We encrypt the DRIS passed to the API. Patch the CryptDRIS function in this file from source code generated by DinkeyAdd.}
Function ProtCheckWithAlgEnc : LongInt;
var
    dris: drisType;

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

    CryptDRIS(@dris, dris.seed1, dris.seed2);   { encrypt DRIS - !!!!you should separate from DDProtCheck for greater security }

    result := DDProtCheck(@dris, NIL);

    CryptDRIS(@dris, dris.seed1, dris.seed2);   { decrypt DRIS - !!!!you should separate from DDProtCheck for greater security }

    if (result <> 0) then
    begin
        DisplayError(result, dris.ext_err);	 { display messages for any errors that have occurred }
        exit;
    end;

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

    if (dris.sdsn <> MY_SDSN) then
    begin
        messagedlg('Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { later on in your program you can check the return code again }
    if (dris.ret_code <> 0) then
    begin
        messagedlg('Dinkey Dongle protection error.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { 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) then
    begin
        messagedlg('Dinkey protection error! You have not patched your algorithm in the MyAlgorithm routine', mtError, [mbok], 0);
        result := -1;
        exit;
    end;
end;

{ **************************  WriteBytesEnc************************************** }
{ This writes the Data 012...9 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 18 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.}
Function WriteBytesEnc : LongInt;
var
    dris: drisType;
    DataToWrite : packed array[0..9] of byte;
    alg_ans, i: longint;

begin
    for i := 0 to 9 do
        DataToWrite[i] := i;
    { set the DRIS to have random values }
    random_set(@dris, SizeOf(dris));            { NB this also fills seed1, seed2, var_a...var_h with random values }
    { then set the values we want to use }
    Move(AnsiString('DRIS'), dris.header, 4);
    dris.size:= SizeOf(dris);
    dris.myfunction:= WRITE_DATA_AREA;           { standard protection check & write data}
    dris.flags:= 0;                              { no extra flags, but you may want to specify some if you want to start a network user or decrement execs,... }
    dris.rw_offset := 7;
    dris.rw_length := SizeOf(DataToWrite);
    dris.rw_data_ptr := Addr(DataToWrite);

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

    { encrypt data we want to write }
    CryptApiData(@DataToWrite, SizeOf(DataToWrite), dris.seed1, dris.seed2, alg_ans);

    CryptDRIS(@dris, dris.seed1, dris.seed2);    { encrypt DRIS - !!!!you should separate from DDProtCheck for greater security }

    { call dongle-checking code }
    result := DDProtCheck(@dris, NIL);

    CryptDRIS(@dris, dris.seed1, dris.seed2);   { decrypt DRIS - !!!!you should separate from DDProtCheck for greater security }

    if (result <> 0) then
    begin
        DisplayError(result, dris.ext_err);	 { display messages for any errors that have occurred }
        exit;
    end;

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

    if (dris.sdsn <> MY_SDSN) then
    begin
        messagedlg('Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { later on in your program you can check the return code again }
    if (dris.ret_code <> 0) then
    begin
        messagedlg('Dinkey Dongle protection error.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;
end;

{ **************************  ReadBytesEnc  *************************************** }
{ This reads 10 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 18 bytes long.
  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.}
Function ReadBytesEnc : LongInt;
var
    dris: drisType;
    DataToRead : packed array[0..9] of byte;
    display : string;
    alg_ans, i: longint;

begin
    { set the DRIS to have random values }
    random_set(@dris, SizeOf(dris));             { NB this also fills seed1, seed2, var_a...var_h with random values }
    { then set the values we want to use }
    Move(AnsiString('DRIS'), dris.header, 4);
    dris.size:= SizeOf(dris);
    dris.myfunction:= READ_DATA_AREA;            { standard protection check & write data}
    dris.flags:= 0;                              { no extra flags, but you may want to specify some if you want to start a network user or decrement execs,... }
    dris.rw_offset := 7;
    dris.rw_length := SizeOf(DataToRead);
    dris.rw_data_ptr := Addr(DataToRead);

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

    CryptDRIS(@dris, dris.seed1, dris.seed2);   { encrypt DRIS - !!!!you should separate from DDProtCheck for greater security }

    { call dongle-checking code }
    result := DDProtCheck(@dris, NIL);

    CryptDRIS(@dris, dris.seed1, dris.seed2);   { decrypt DRIS - !!!!you should separate from DDProtCheck for greater security }

    if (result <> 0) then
    begin
        DisplayError(result, dris.ext_err);	 { display messages for any errors that have occurred }
        exit;
    end;

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

    if (dris.sdsn <> MY_SDSN) then
    begin
        messagedlg('Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { later on in your program you can check the return code again }
    if (dris.ret_code <> 0) then
    begin
        messagedlg('Dinkey Dongle protection error.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

   { decrypt data that was read }
   CryptApiData(@DataToRead, SizeOf(DataToRead), dris.seed1, dris.seed2, alg_ans);
   display := 'Dinkey Dongle data area contains: ';
   for i:=0 to 9 do
        display := display + IntToStr(DataToRead[i]) + ' ';
   messagedlg(display, mtInformation, [mbok], 0);
end;

{ **************************  EncryptUserDataEnc  ******************************* }
{ This function will do a protection check and ask the dongle to encrypt some data using encryption key 1
  It will then do another protection & decrypt the data.
  We encrypt the DRIS and Data passed to/from the API. Patch the CryptDRIS and CryptApiData functions in this file from source code generated by DinkeyAdd.}
Function EncryptUserDataEnc : LongInt;
var
    dris: drisType;
    display : string;
    DataBytes : packed array[0..9] of byte;
    alg_ans,i : LongInt;

begin
    for i := 0 to 9 do
        DataBytes[i] := i;                      { initialise data to 0,1,2,3,4,5,6,7,8,9}
    { set the DRIS to have random values }
    random_set(@dris, SizeOf(dris));            { NB this also fills seed1, seed2, var_a...var_h with random values }
    { then set the values we want to use }
    Move(AnsiString('DRIS'), dris.header, 4);
    dris.size:= SizeOf(dris);
    dris.myfunction:= ENCRYPT_USER_DATA;         { standard protection check & 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 := SizeOf(DataBytes);
    dris.rw_data_ptr := Addr(DataBytes);

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

    { encrypt data we want to write }
    CryptApiData(@DataBytes, Sizeof(DataBytes), dris.seed1, dris.seed2, alg_ans);

    CryptDRIS(@dris, dris.seed1, dris.seed2);   { encrypt DRIS - !!!!you should separate from DDProtCheck for greater security }

    { call dongle-checking code }
    result := DDProtCheck(@dris, NIL);

    CryptDRIS(@dris, dris.seed1, dris.seed2);   { decrypt DRIS - !!!!you should separate from DDProtCheck for greater security }

    if (result <> 0) then
    begin
        DisplayError(result, dris.ext_err);	 { display messages for any errors that have occurred }
        exit;
    end;

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

    { Now decrypt the same data to get the original values }
    random_set(@dris, SizeOf(dris));
    { then set the values we want to use }
    Move(AnsiString('DRIS'), dris.header, 4);
    dris.size:= SizeOf(dris);
    dris.myfunction:= DECRYPT_USER_DATA;         { standard protection check & 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 := SizeOf(DataBytes);
    dris.rw_data_ptr := Addr(DataBytes);

    CryptDRIS(@dris, dris.seed1, dris.seed2);    { encrypt DRIS - !!!!you should separate from DDProtCheck for greater security }

    { call dongle-checking code }
    result := DDProtCheck(@dris, NIL);

    CryptDRIS(@dris, dris.seed1, dris.seed2);    { decrypt DRIS - !!!!you should separate from DDProtCheck for greater security }

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

    if (dris.sdsn <> MY_SDSN) then
    begin
        messagedlg('Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    { later on in your program you can check the return code again }
    if (dris.ret_code <> 0) then
    begin
        messagedlg('Dinkey Dongle protection error.', mtError, [mbok], 0);
        result := -1;
        exit;
    end;

    alg_ans := MyRWAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h);
    { decrypt data that was read }
    CryptApiData(@DataBytes, SizeOf(DataBytes), dris.seed1, dris.seed2, alg_ans);
    display := 'Data decrypted by dongle is: ';
    for i:=0 to 9 do
        display := display + IntToStr(DataBytes[i]) + ' ';
    messagedlg(display, mtInformation, [mbok], 0);
end;

{ *************** code that gets executed on startup *********************** }
begin
  {call the function(s) of your choice here - in this example I have chosen a standard protection check}
  if (ProtCheck() <> 0) then
     halt;
end.

