A Data Encryption routine for UNIFACE

Tony Marston - 10th July 2000

Anybody who has wished to encrypt data within a UNIFACE application has probably given up any attempt to write it in UNIFACE and has opted instead for a 3GL solution. This is OK if you have access to suitable 3GL resources, but not everybody does. I have often wondered if it was possible to write any sort of encryption routine within UNIFACE, and after a bit of trial and error I have eventually succeeded. You may find my efforts to be of some use to you.

1. Rotating each character a fixed number of positions
The only way to scramble a string of characters using nothing but standard UNIFACE proc code is to swap each character for another one. One way to do this is to convert each character into a number, add another number to it, then convert it back into a different character from the same sequence. This is how the ROT13 system works as it rotates each character 13 positions. This system is easy to break.
2. Swapping between two different character strings
A better method is to use two different character strings - one for obtaining the index number of the source character, and a second for converting this index number into a different target character. The advantage is that there is no fixed rotation between one character and the next, which would make it difficult for any potential hacker. The disadvantage is that each source character will always be converted to the same target character, so once a source and target pair have been found they will always remain the same. This is not good.
3. Manipulating the index number between the two strings
This involves changing the index number obtained from STRING1 before applying it to STRING2, but using a value that is generated at run time according to some fixed rules. The rules must be fixed so that the same source string will always result in the same target string, otherwise it will not be possible to verify a password with one that is held on file. The method I use is to have a user-definable key (e.g. user id) entered at the same time as the source string (e.g. user password). I then convert each letter of the key into a number providing me with a list of numbers. Before I use the index number obtained from STRING1 on STRING2 I add in one of the values from the list of key numbers. As it is a list I can extract the first value, drop it, then put it back at the end of the list, thus rotating through all the entries one at a time. This means that the same password with different user id's will always result in a different encryption string. It also means that if the source string contains the same character more than once it is highly unlikely that the same target character will be extracted from STRING2.
4. Customising the encryption algorithm
There are two main ways in which this method can be customised so that each implementation is totally unique. The first is in the scrambling of the second string. There are 91 usable characters in the font 0 character set, so there are a large number of possible combinations.

The second way is to apply another 'fudge factor' to the index number before using it to extract from STRING2. You can add another number to the list obtained from the key. A method contained in my source code is to accumulate all the key numbers and put this total at the start of the list. Another method is to change each key number before it is put back at the end of the list, such as making it negative if it is divisible by 3, or simply adding 1.5 each time. The possible permutations are countless.

The important thing is that the same combination of user id and password will always yield the same encrypted value, but that a hacker will have his work cut out to break the code (I hope!).

Here is a screen shot of the sample program:

encrypt.gif

In this example the password field has been padded out to its maximum length (16 bytes) with trailing spaces. Note that each of these trailing spaces has been converted to a different character.

The sample source code contained in my web site includes routines for both encryption and decryption. Try it for yourself and see. For those of you who are too lazy to download the source code here it is:

entry ENCRYPT
params
   string  pi_UserKey  : IN
   string  pio_Target  : INOUT
endparams
variables
   string  lv_Source
   string  lv_Char1
   numeric lv_Num1
   string  lv_Char2
   numeric lv_Num2
   numeric lv_KeyNum
   numeric lv_Factor
   numeric lv_ScrambleLen
endvariables

NOTE: the local constants are defined as follows:
<SCRAMBLE1> =! %%"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz
<SCRAMBLE2> =&q13`h5w_79f^jAE]okI\OzU[24p@6s8?BgP>dFV=m D<TcS%Zer:lGK/uCy.JLt-RMa,NvW+Ynb*0Xx)HiQ!%%"#$'(;
End note

$Scramble1$ = "<SCRAMBLE1>"    ; see note above
$Scramble2$ = "<SCRAMBLE2>"    ; see note above

length $Scramble1$
lv_ScrambleLen = $result       ; store length of string
length $Scramble2$
if ($result != lv_ScrambleLen)
   message "$scramble1$ not same length as $scramble2$"
   return(-1)
endif

; convert UserKey into a sequence of numbers
call LP_CONV_USERKEY(pi_UserKey,$FactorList$)
if ($status) return($status)

lv_Source   = pio_Target       ; copy
pio_Target  = ""               ; initialise

while (lv_Source != "")
   lv_Char1 = lv_Source[1:1]   ; extract 1st character
   lv_Source = lv_Source[2]    ; drop 1st character
   scan $Scramble1$,lv_Char1   ; find char in $Scramble1$
   if ($result = 0)
       message "cannot find '%%lv_Char1' in $scramble1$"
       return(-1)
   endif
   lv_Num1 = $result

   call LP_GET_KEYNUM(lv_KeyNum)               ; from $FactorList$

   lv_Factor = lv_Factor + lv_KeyNum           ; add to factor
   lv_Num2 = lv_Num1 + lv_Factor               ; apply adjustment
   call LP_CHECK_RANGE(lv_Num2,lv_ScrambleLen) ; ensure it's within range
   lv_Factor = lv_Factor + lv_Num2             ; add to factor

   lv_Char2 = $Scramble2$[lv_Num2:1]           ; extract new character
   pio_Target = "%%pio_Target%%lv_Char2"       ; append to target

endwhile

return(0)

end ENCRYPT
;==============================================================================
entry LP_CONV_USERKEY  ; convert key into a list of numbers
params
   string  pi_UserKey          : IN
   string  po_ListOfNumbers    : OUT
endparams
variables
   string  lv_Char
   numeric lv_Length
   numeric lv_Total
endvariables

if (pi_UserKey = "")
   message "Key for encryption/decryption is empty"
   return(-1)
endif

po_ListOfNumbers = ""              ; initialise output
putitem po_ListOfNumbers, 1, "0"   ; reserve 1st number in list

length pi_UserKey                  ; get length of input string
lv_Length = $result

; convert string into a sequence of numbers
while (pi_UserKey != "")
   lv_Char = pi_UserKey[1:1]       ; extract 1st character
   pi_UserKey = pi_UserKey[2]      ; remove 1st character

   ; find this char's position in $Scramble1$ and add to output list
   scan $Scramble1$,lv_Char
   if ($result > 0)
       putitem po_ListOfNumbers, -1, $result
       lv_Total = lv_Total + $result   ; accumulate values
   else
       ; character not found! ignore it (should never happen!)
   endif
endwhile

lv_Total = lv_Total - lv_Length        ; subtract number of characters
putitem po_ListOfNumbers, 1, lv_Total  ; 1st number is grand total

return(0)

end LP_CONV_USERKEY
;==============================================================================
entry LP_GET_KEYNUM     ; get index number from $factorlist$
params
   numeric  po_KeyNum   : OUT
endparams
variables
   numeric  lv_KeyNum
endvariables

; pass back the first number from $FactorList$ (whole numbers only)
getitem lv_KeyNum, $FactorList$, 1          ; get 1st number
po_KeyNum = lv_KeyNum[round]                ; return the value rounded

if (po_KeyNum%3 = 0)                        ; if it is divisible by 3
   po_KeyNum = po_KeyNum * -1               ; make it negative
endif

; Note that the number is adjusted before being put back into the list.
; Decimal places are allowed in the adjustment as they are rounded before
; being returned to the calling module.
; This produces an irregular effect to further confuse any hacker.

delitem $FactorList$, 1                     ; drop from front of list
lv_KeyNum = lv_KeyNum + 1.75                ; change it
putitem $FactorList$, -1, lv_KeyNum         ; put back at end of list

end LP_GET_KEYNUM
;==============================================================================
entry LP_CHECK_RANGE   ; check that number is within range
                       ; (not < 1, not > limit)
params
   numeric  pio_Number : INOUT
   numeric  pi_Limit   : IN
endparams

pio_Number = pio_Number[round]     ; must be a whole nunmber

while (pio_Number > pi_Limit)      ; test for above range
   pio_Number = pio_Number - pi_Limit
endwhile

while (pio_Number < 1)             ; test for below range
   pio_Number = pio_Number + pi_Limit
endwhile

end LP_CHECK_RANGE
;==============================================================================
entry DECRYPT
params
   string  pi_UserKey  : IN
   string  pio_Target  : INOUT
endparams
variables
   string  lv_Source
   string  lv_Char1
   numeric lv_Num1
   string  lv_Char2
   numeric lv_Num2
   numeric lv_KeyNum
   numeric lv_Factor
   numeric lv_ScrambleLen
endvariables

$Scramble1$ = "<SCRAMBLE1>"    ; see note above
$Scramble2$ = "<SCRAMBLE2>"    ; see note above

length $Scramble1$
lv_ScrambleLen = $result       ; store length of string
length $Scramble2$
if ($result != lv_ScrambleLen)
   message "$scramble1$ not same length as $scramble2$"
   return(-1)
endif

; convert UserKey into a sequence of numbers
call LP_CONV_USERKEY(pi_UserKey,$FactorList$)
if ($status) return($status)

length $Scramble1$
lv_ScrambleLen = $result       ; store length of string

lv_Source   = pio_Target       ; copy
pio_Target  = ""               ; initialise

while (lv_Source != "")
   lv_Char2 = lv_Source[1:1]   ; extract 1st character
   lv_Source = lv_Source[2]    ; drop 1st character
   scan $Scramble2$,lv_Char2   ; find char in $Scramble2$
   if ($result = 0)
       message "cannot find '%%lv_Char2' in $scramble2$"
       return(-1)
   endif
   lv_Num2 = $result

   call LP_GET_KEYNUM(lv_KeyNum)               ; from $FactorList$

   lv_Factor = lv_Factor - lv_KeyNum           ; subtract from factor
   lv_Num1 = lv_Num2 + lv_Factor               ; apply adjustment
   call LP_CHECK_RANGE(lv_Num1,lv_ScrambleLen) ; ensure it's within range
   lv_Factor = lv_Factor - lv_Num2             ; subtract from factor

   lv_Char1 = $Scramble1$[lv_Num1:1]           ; extract new character
   pio_Target = "%%pio_Target%%lv_Char1"       ; append to target

endwhile

; remove trailing spaces from output string
length pio_Target
lv_Num1 = $result
while (pio_Target[lv_Num1:1] = " ")            ; if last char is a space
   lv_Num1 = lv_Num1 - 1
   pio_Target = pio_Target[1:lv_Num1]          ; remove it
endwhile

return(0)

end DECRYPT

Tony Marston
10th July 2000

mailto:tony@tonymarston.net
mailto:TonyMarston@hotmail.com
http://www.tonymarston.net

counter