Recursive XOR encode()/decode procedures

Share your advanced PureBasic knowledge/code with the community.
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Recursive XOR encode()/decode procedures

Post by utopiomania »

Code updated for 5.20+

I'm experimenting with software protection and thought I should post this because it can be useful for other
uses too.

It's a pair of recursive, complimentary encode()/decode() procedures intended to encode and decode a string
using the same random key sequence using bitwise XOR.

The encoder is supposed to decode a string, write the output to a placeholder in a program, then the decode
procedure restores the info at runtime to be displayed in the program.

Heh, as I said it's experimenting :P and I 'think' it can be quite safe, but don't know. I assume the recursive
nature of it makes it more difficult to follow the logic tracing the assembly too.

Code: Select all

Global pos, lim

Procedure encode(*mem)
  If pos <= lim
    PokeC(*mem + pos, PeekC(*mem + pos) ! Random(255))
    pos + 1
    encode(*mem)
  Else
    ProcedureReturn
  EndIf
EndProcedure

Procedure decode(*mem)
  If pos <= lim
    PokeC(*mem + pos, PeekC(*mem + pos) ! Random(255))
    pos + 1
    decode(*mem)
  Else
    ProcedureReturn
  EndIf
EndProcedure

;run
what.s = "USERNAME USERNAME USERNAME USERNAME USERNAME USERNAME"
lim = StringByteLength(what)

*mem = AllocateMemory(lim)
*ptr = *mem
CopyMemoryString(@what, @*ptr)

Debug "Entered into protector:"
Debug what

RandomSeed(128)
encode(*mem)
Debug "written into placeholder:"
Debug PeekS(*mem)

pos = 0
RandomSeed(128)
decode(*mem)
Debug "Displayed in aboutbox:"
Debug PeekS(*mem)

End
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Looks like a XOR based stream chiper.
http://en.wikipedia.org/wiki/XOR_cipher
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Post by utopiomania »

Yes, the article seems to describe it correctly.
However, if the key is as long as the message (so it is never repeated) and its bits are random, it is in effect a one-time pad (also known as Vernam cipher), which is unbreakable in theory.
Note that my procedures in fact are using a random key as long as the message, but that the random sequences are initiated in 'both ends' by the same random seeds.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

some stuff that might be usefull to someone...

Code: Select all

Procedure.l x_crypt(*channel.x_crypt,dat.l)
  Protected mode.l, key_p.l, key_c.l, key_l.l, old.l, n.l
  Global x_retval.l
  ;
  ; *** en- or decrypt a single element
  ;
  ; in:       *channel.x = 0..7  - 8 structures handled by the x_crypt() routines themselves
  ;                      = n     - the memory address of a struct defining the behaviour
  ;           dat.l              - data to en- or decrypt
  ; retval.l                     - de- or encrypted data
  ; out:      x_retval.l         - same as retval
  ;
  ; all information related to the encryption mechanism is stored in a structure, so multiple
  ; crypto sets can run in parallel, all necessary fields have to be set before first time
  ; calling x_crypt()
  ;
  ; typically, one will have to set for x_crypt():
  ;
  ; ...\mode
  ; ...\key_p = address of first element in key (see note below regarding using strings as keys)
  ; ...\key_l = number of elements in key (length in bytes)
  ; ...\key_c = 0
  ; ...\old   = 0
  ; ...\n     = 0
  ;
  ; for de/encrypting blocks, the following fields are also important:
  ;
  ; ...\mem_p = address of first element in block to process
  ; ...\mem_l = length of block
  ; ...\mem_c = 0
  ;
  ; note 1: the routine x_crypt_set() sets these fields
  ;
  ; note 2: it is perfectly possible to use a string to contain the key, but remember that
  ; the pointer to this string (key_p) may change due to garbage recollection! so get the
  ; memory location of the key string every time before calling the x_crypt routines using @<stringvar>
  ;
  ; note 3: you can either define your own structs and point to those, or use an 'identifier' 0..7
  ; when using 0..7 the x_crypt() routines maintain their own buffers
  ;
  ; sample usage:
  ;
  ;    x_init();
  ;    ;
  ;    s.s = "This is a string to encrypt, including 1234567890 and ##########."
  ;    s_l = Len(s)
  ;    ;
  ;    x_crypt_set(1,#x_crypt_blue1,0,0,0,0,"","This is a key.")            ; first channel with key
  ;    x_crypt_text(1,s)                                                    ; encrypt the text, channel is going to create a buffer
  ;    ;
  ;    Debug x_peekhex(x_crypt_channel(1)\mem_p,x_crypt_channel(1)\mem_l,1)
  ;    Debug PeekB(x_crypt_channel(1)\mem_p)
  ;    Debug PeekS(x_crypt_channel(1)\mem_p,s_l)
  ;    Debug ""
  ;    Debug x_crypt_channel(1)\key_c
  ;    Debug x_crypt_channel(1)\mem_p
  ;    Debug x_crypt_channel(1)\mem_l
  ;    Debug x_crypt_channel(1)\key_p
  ;    Debug x_crypt_channel(1)\key_l
  ;    Debug ""
  ;    ;
  ;    x_crypt_set(2,#x_crypt_blue1,0,0,0,0,"","This is a key.")            ; second channel with same key
  ;    x_crypt_block(2,x_crypt_channel(1)\mem_p,s_l)                        ; note that channel 2 doesn't have its own memory block
  ;    ;
  ;    Debug x_peekhex(x_crypt_channel(1)\mem_p,x_crypt_channel(1)\mem_l,1)
  ;    Debug PeekS(x_crypt_channel(1)\mem_p,s_l)
  ;    Debug ""
  ;    ;
  ;    x_crypt_text(1,s)                                                    ; do it again, the hex result should be different from the first call
  ;    ;
  ;    Debug x_peekhex(x_crypt_channel(1)\mem_p,x_crypt_channel(1)\mem_l,1)
  ;    Debug PeekS(x_crypt_channel(1)\mem_p,s_l)
  ;    Debug ""
  ;    Debug x_crypt_channel(1)\key_c
  ;    Debug x_crypt_channel(1)\mem_p
  ;    Debug x_crypt_channel(1)\mem_l
  ;    Debug x_crypt_channel(1)\key_p
  ;    Debug x_crypt_channel(1)\key_l
  ;    Debug ""
  ;    ;
  ;    x_crypt_block(2,x_crypt_channel(1)\mem_p,s_l)                        ; and decrypt it again using the second channel
  ;    ;
  ;    Debug x_peekhex(x_crypt_channel(1)\mem_p,x_crypt_channel(1)\mem_l,1)
  ;    Debug PeekS(x_crypt_channel(1)\mem_p,s_l)
  ;    Debug ""
  ;
  ;
  ; time to code... first move struct elements to vars, it's probably slower but makes code easier to read
  ;
  If *channel < 0
  ElseIf *channel <= 7
    x_crypt(@x_crypt_channel(*channel),dat.l)
  Else
    ;
    old = *channel\old             ; previously processed element (byte, word, whatever)
    key_p = *channel\key_p         ; pointer to first byte of key
    key_l = *channel\key_l         ; length of key
    key_c = *channel\key_c         ; counter (pointer to current element of key)
    mode = *channel\mode           ; what to do (type of encoding, how to encode, etc.)
    n = *channel\n                 ; counter, number of bytes encrypted
    ;
    Select mode
    Case #x_crypt_none
      ;
      ; - for debugging purposes
      ;
      x_retval = dat
      ;
    Case #x_crypt_invert
      ;
      ; - symmetric (encoding algoritm is the same as decoding algoritm)
      ; - very insecure
      ;
      x_retval = (dat ! $FF)
      ;
    Case #x_crypt_count_encode
      ;
      ; - for debugging purposes
      ;
      key_c = key_c+1
      x_retval = (dat+key_c) & $FF
      ;
    Case #x_crypt_count_decode
      ;
      ; - for debugging purposes
      ;
      key_c = key_c+1
      x_retval = (dat-key_c) & $FF
      ;
    Case #x_crypt_keyxor
      ;
      ; this is a simple encryption mechanism that xor's the original string with the key
      ;
      ; - symmetric
      ; - continuous (ie. repeated calls will use a previous position of the key pointer)
      ; - poor security
      ;
      x_retval = ( dat ! PeekB(key_p+key_c) ) & $FF
      key_c = key_c+1
      ;
    Case #x_crypt_blue1
      ;
      ; this is a more complex mechanism that uses previously processed data with key and counter
      ; and should at least be confusing to hackers...
      ;
      ; - symmetric
      ; - continuous
      ; - reasonably secure (but don't use it to scramble your bank account data :-))
      ;
      x_retval = ( key_c ! old ! dat ! PeekB(key_p+key_c) ) & $FF
      key_c = key_c+1
      old = x_rolb(x_retval)
      ;
    EndSelect
    ;
    If key_c >= key_l
      key_c = 0
    EndIf
    n = n+1
    ;
    ; store all (changed) data back in the struct
    ;
    *channel\key_c = key_c
    *channel\old = old
    *channel\n = n
    ;
  EndIf
  ;
  ProcedureReturn x_retval
EndProcedure

Procedure.l x_crypt_block(*channel.x_crypt,mem_p,mem_l)
  Global x_retval_p.l, x_retval_l.l, x_retval_x.l
  ;
  ; *** in memory encryption of given data block using specified struct for method and parameters
  ;
  ; in:     *channel.x_crypt = 0..7   - identifier of channel
  ;                        = n        - pointer to structure to use
  ;         mem_p.l        = n        - start of block to en/decrypt, 0 to use address in channel struct
  ;         mem_l.l        = n        - length of block, 0 to use length in channel struct
  ; retval:                           - none
  ; out:    x_retval_p.l   = n        - pointer to processed block
  ;         x_retval_l.l   = n        - length of processed block
  ;
  If *channel < 0
  ElseIf *channel < 8
    If mem_p < 1
      mem_p = x_crypt_channel(*channel)\mem_p
    EndIf
    If mem_l < 1
      mem_l = x_crypt_channel(*channel)\mem_l
    EndIf
    x_crypt_block(@x_crypt_channel(*channel),mem_p,mem_l)
  Else
    ;
    n = 0
    While n < mem_l
      PokeB(mem_p+n,x_crypt(*channel,PeekB(mem_p+n)))
      n = n+1
    Wend
    ;
    x_retval_p = mem_p
    x_retval_l = mem_l
    ;
  EndIf
  ;
EndProcedure

Procedure.l x_crypt_text(*channel.x_crypt,string.s)
  Protected l.l
  Global x_retval_p.l, x_retval_l.l, x_retval_x.l
  ;
  ; *** use settings of previous x_crypt_set or specified structure to process given string
  ;
  ; in:     *channel.x_crypt = 0..7   - channel
  ;                        = n        - pointer to structure
  ;         string.s                  - string to place
  ; retval:                           - none
  ; out:    x_retval_p                - pointer to processed block
  ;         x_retval_l                - length of processed block
  ;
  l = Len(string)
  If *channel < 0
  ElseIf *channel < 8
    FreeMemory(x_crypt_channel(*channel)\mem_p)
    x_crypt_channel(*channel)\mem_p = AllocateMemory(l)
    x_pokes(x_crypt_channel(*channel)\mem_p,string,l)
    x_crypt_channel(*channel)\mem_l = l
    x_crypt_block(*channel,-1,-1)
  Else
    x_pokes(*channel\mem_p,string,l)
    *channel\mem_l = l
    x_crypt_block(*channel,-1,-1)
  EndIf
  ;
EndProcedure

Procedure.s x_crypt_gettext(*channel.x_crypt)
  ;
  ; *** get decrypted string
  ;
  ; in:     *channel.x_crypt = 0..7   - channel to use, or...
  ;                          = n      - pointer to x_crypt structure
  ; retval: .s                        - string containing (de)crypted data
  ; out:    x_retval_p.l     = n      - pointer to data
  ;         x_retval_l.l     = n      - length of data
  ;
  If *channel < 0
  Else
    x_crypt_block(*channel,-1,-1)
    ProcedureReturn PeekS(x_retval_p,x_retval_l)
  EndIf
  ;
EndProcedure

Procedure x_crypt_set(*channel.x_crypt,mode.l,mem_p.l,mem_l.l,key_p.l,key_l.l,string.s,key.s)
  ;
  ; *** set encryption / decryption parameters
  ;
  ; in: *channel.x_crypt = 0..7 - if < 8 an identifier or...
  ;                    = n      - if > 7 a memory address of a structure that controls the en/decryption behaviour
  ;
  ;     for 'channel' 0..7:     mem_p.l  = -1  - keep old memory pointer, counter, and data intact
  ;                             mem_l.l  > 0   - wipe and resize data buffer to given size
  ;                             key_p.l  = -1  - keep old key pointer, counter, And Data intact
  ;                             key_l.l  > n   - resize key buffer to given size
  ;                             string.s > ""  - overwrite data with given string, adjust mem_l and mem_p
  ;                             key.s    > ""  - overwrite key with given string, adjust key_l and key_p
  ;
  ;     for given structure:    mem_p.l  = n   - set start data block
  ;                             mem_l.l  = n   - set size data block
  ;                             key_p.l  = n   - set start of key
  ;                             key_l.l  = n   - set length of key
  ;                             string.s > ""  - overwrite data with given string, adjust mem_l
  ;                             key.s    > ""  - overwrite key with given string, adjust key_l
  ;
  ; note: when specifying the structs you have to make sure the buffers pointed to are of sufficient size!
  ;
  If *channel < 0
  ElseIf *channel < 8
    If mem_p = -1
      mem_p = x_crypt_channel(*channel)\mem_p
      mem_l = x_crypt_channel(*channel)\mem_l
    Else
      mem_p = x_crypt_channel(*channel)\mem_p
      mem_l = x_max(mem_l,Len(string))
      If mem_l > 0
        If x_crypt_channel(*channel)\mem_p <> 0
          FreeMemory(mem_p)
        EndIf
        mem_p = AllocateMemory(mem_l+1)
      EndIf
    EndIf
    If key_p = -1
      key_p = x_crypt_channel(*channel)\key_p
      key_l = x_crypt_channel(*channel)\key_l
    Else
      key_p = x_crypt_channel(*channel)\key_p
      key_l = x_max(key_l,Len(key))
      ;
      If key_l > 0
        If x_crypt_channel(*channel)\key_p <> 0
          FreeMemory(key_p)
        EndIf
        key_p = AllocateMemory(key_l+1)
      EndIf
    EndIf
    x_crypt_set(@x_crypt_channel(*channel),mode,mem_p,mem_l,key_p,key_l,string,key)
  Else
    ;
    l = Len(string)
    If l > 0 And mem_p > 0
      x_pokes(mem_p,string,l)
      mem_l = Len(string)
    EndIf
    l = Len(key)
    If l > 0 And key_p > 0
      x_pokes(key_p,key,l)
      key_l = Len(key)
    EndIf
    ;
    *channel\mode = mode                    ; what to do / how to encrypt (see below)
    *channel\mem_p = mem_p                  ; pointer to block of memory to encrypt
    *channel\mem_l = mem_l                  ; length of block
    *channel\mem_c = 0                      ; counter, points to the next byte to encrypt
    *channel\key_p = key_p                  ; pointer to block of memory containing key
    *channel\key_l = key_l                  ; length of key
    *channel\key_c = 0                      ; counter to keep track of next element of the key
    *channel\old = 0                        ; previous processed element (byte, word, whatever)
    *channel\n = 0                          ; counter (how many bytes have been processed, used by some encryption schemes)
    *channel\crc32 = 0                      ; crc32 of data (not used yet)
    *channel\md5 = ""                       ; md5 hash of data (not used yet)
    ;
  EndIf
  ;
EndProcedure
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Post by ricardo »

Lets see if im learning fine.

Here is utopiomania code as Thefoll teach us to do:

Code: Select all

Global Pos, lim 

Procedure EncDec(*mem,rS) 
   RandomSeed(rS) 
   JMP l_label2
   label1:
   PokeC(*mem + Pos, PeekC(*mem + Pos) ! Random(255)) 
   Pos + 1 
   EncDec(*mem,rS) 
   JMP l_label2
   label3:
   PUSH l_label4
   RET
   label2:
   If Pos <= lim 
      PUSH l_label1
   Else 
      PUSH l_label3
   EndIf 
   RET
   label4:
EndProcedure 



;run 
what.s = "USERNAME USERNAME USERNAME USERNAME USERNAME USERNAME" 
lim = Len(what) 

*mem = AllocateMemory(lim) 
*ptr = *mem 
CopyMemoryString(@what, @*ptr) 

Debug "Entered into protector:" 
Debug what 

EncDec(*mem,128) 
Debug "written into placeholder:" 
Debug PeekS(*mem) 

Pos = 0 
EncDec(*mem,128)
Debug "Displayed in aboutbox:" 
Debug PeekS(*mem) 

End 
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Post by ricardo »

Another function mores spaguetized!!

Code: Select all

Procedure EncDec(*mem,rS) 
   JMP l_label5
   label6:
   RandomSeed(rS+1) 
   JMP l_label2
   label1:
   PokeC(*mem + Pos, PeekC(*mem + Pos) ! Random(255)) 
   Pos + 1 
   EncDec(*mem,rS) 
   JMP l_label2
   label3:
   PUSH l_label4
   RET
   label2:
   If Pos <= lim 
      PUSH l_label1
   Else 
      PUSH l_label3
   EndIf 
   RET
   label5:
   If PeekS(*mem) = "fakepassword" ;he he
      JMP l_label7
   EndIf
   PUSH l_label6
   RET
   label7:
   MessageRequester("Ok","password fine"); he he
   End
   label4:
EndProcedure 
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Post by ricardo »

I guess next step should be to hide the RandomSeed number.

Maybe hidding it in the encrypted text?
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Post by ricardo »

Code: Select all

;PB4 version
Global Pos, lim 

Procedure EncDec(*mem,rS) 
   JMP l_label5
   label6:
   RandomSeed(rS+1) 
   JMP l_label2
   label1:
   PokeC(*mem + Pos, PeekC(*mem + Pos) ! Random(255)) 
   Pos + 1 
   EncDec(*mem,rS) 
   JMP l_label2
   label3:
   PUSH l_label4
   RET
   label2:
   If Pos <= lim 
      PUSH l_label1
   Else 
      PUSH l_label3
   EndIf 
   RET
   label5:
   If PeekS(*mem) = "fakepassword" ;he he
      JMP l_label7
   EndIf
   PUSH l_label6
   RET
   label7:
   MessageRequester("Ok","password fine"); he he
   End
   label4:
EndProcedure 

Procedure.s encode(*mem)
   Pos = 0 
   RandomNumber = Random(50) +128
   Debug "RandomNumber " + Str(RandomNumber)
   Debug "Random Char: " + Chr(RandomNumber) + " will be the first!"
   EncDec(*mem,RandomNumber)
   Todo$ = Chr(RandomNumber) + PeekS(*mem)
   PokeS(*mem,Todo$)
   ProcedureReturn Todo$
EndProcedure

Procedure.s decode(*mem)
   Pos = 0 
   RandomNumber  = Asc(Left(PeekS(*mem),1))
   Debug "Decode RandomNumber " + Str(RandomNumber)
   SetWindowTitle(0,Str(RandomNumber))
   Todo$ = Right(PeekS(*mem),lim-1)
   PokeS(*mem,Todo$)
   EncDec(*mem,RandomNumber)
   ProcedureReturn PeekS(*mem)
EndProcedure



If OpenWindow(0,100,150,450,200,"Test",#PB_Window_SystemMenu)
   CreateGadgetList(WindowID(0))
   StringGadget(1,10,10,350,20,"myemail@myprovider.com")
   ButtonGadget(2,370,10,50,20,"Encrypt")
   
   StringGadget(3,10,70,350,20,"")
   ButtonGadget(4,370,70,50,20,"Decrypt")
   DisableGadget(4,1)
   Repeat
      EventID=WaitWindowEvent()
      
      Select EventID
         
         Case #PB_Event_Gadget
            Select EventGadget()
               Case 2
                  If GetGadgetText(1)
                     DisableGadget(2,1)
                     DisableGadget(4,0)
                     what.s = GetGadgetText(1)
                     lim = Len(what)
                     FreeMemory(-1)
                     *mem = AllocateMemory(lim) 
                     *ptr = *mem 
                     CopyMemoryString(@what, @*ptr) 
                     SetGadgetText(3,encode(*mem))
                  EndIf
               Case 4
                  If GetGadgetText(3)
                     DisableGadget(2,0)
                     DisableGadget(4,1)
                     what.s = GetGadgetText(3)
                     lim = Len(what) 
                     FreeMemory(-1)
                     *mem1 = AllocateMemory(lim) 
                     *ptr1 = *mem1 
                     CopyMemoryString(@what, @*ptr1) 
                     MessageRequester("",decode(*mem1))
                  EndIf
            EndSelect
            
      EndSelect
      
   Until EventID=#PB_Event_CloseWindow
EndIf

End 
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Post by utopiomania »

The random seed is indeed a problem, since only one byte needs to be discovered to decode the string.

I imagine that it boils down to isolating the spot where the 'final' randomseed is eiter pushed to the stack
or assigned to a register before the function call to randomseed.

This code spot can be heavily virtualised, but I think it's quite important for the protection to be as strong
as possible before virtualising the code in any case.

But there are some good posts about using the API for encrypting instead:
http://www.purebasic.fr/english/viewtop ... 41&start=0

The code samples in Hexor's second and third posts seems to work very well.

I've learned another lesson too, that knowing a bit of machine code/assembly and reading cracker tutorials
is really necessary to avoid the worst mistakes in trying to write code like this :) :)
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Post by utopiomania »

Here's an idea, remember that it's a form of protection where user details can be
read in the program, for example 'Licenced to:...'

The seller creates a username and serial for the customer, generates a MD5 fingerprint
from them, encrypt the fingerprint and store both in the program.

If they don't match after decoding the md5, procedure one() will introduce random errors
in the program logic depending on how and where you use this procedure in the code.

If as little as char in the user detail string is changed, the program will behave very
badly, if the same is done to the stored encrypted MD5 fingerprint, the errors can be
subtle and appear random, so a cracker might not even notice he is fiddling with it.

I've introduced one error into the keyfile. If you run it several times with debug on,
it will fail to execute correctly from time to time.

If you correct the keyfile, ("A.. -> "C..) and mess with the string username, it won't
work at all.

Code: Select all

#CALG_MD5 = 4 << 13 | 0 | 3 
#CALG_RC4 = 3 << 13 | 4 << 9 | 1
#MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0" 

global *fpp1, *fpp2

procedure.s decrypt(string.s, key.s) 
  protected *buffer 
  
  *buffer = allocateMemory(1024) 
  result.s = "" 
  o.l = 0 
  for i = 1 to len(string) step 2 
    hi.l = asc(mid(string, i, 1)) - 48 
    if hi > 9 
      hi - 7 
    endIf 
    lo.l = asc(mid(string, i + 1, 1)) - 48 
    if lo > 9 
      lo - 7 
    endIf 
    pokeB(*buffer + o, (hi * 16) + lo) 
    o + 1 
  next i 
  dataLength = len(string) / 2 
  
  if cryptAcquireContext_(@hProv, #NULL, #MS_DEF_PROV, 1, 0) = 0 
    cryptAcquireContext_(@hProv, #NULL, #MS_DEF_PROV, 1, 8) 
  endIf 
  if hProv 
    cryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash) 
    if hHash 
      cryptHashData_(hHash, @key, len(key), 0) 
      cryptDeriveKey_(hProv, #CALG_RC4, hHash, 1, @hKey) 
      if hKey 
        if cryptDecrypt_(hKey, 0, #TRUE, 0, *buffer, @dataLength) 
          result = peekS(*Buffer) 
        endIf 
        cryptDestroyKey_(hKey) 
      endIf 
      cryptDestroyHash_(hHash) 
    endIf 
    cryptReleaseContext_(hProv, 0) 
  endIf 
  freeMemory(*buffer) 
  procedureReturn result 
endProcedure

procedure one()
  pos = random(31)
  lhs = peekC(*fpp1 + pos)
  rhs = peekC(*fpp2 + pos)
  if lhs = rhs
    procedureReturn 1
  else
    procedureReturn 0
  endIf
endProcedure

username.s = "username"
fp1.s = MD5Fingerprint(@username, len(username))
*fpp1 = @fp1
;decrypt("C.. correct key
md5.s = decrypt("AEA3936F000FC29A73704038068767FA29FE72B6179B1ECF9F31218A0F6374F4", "abcd")
*fpp2 = @md5

;one() is added all over the place to introduce random errors in the program logic
;as soon as the username details, or keyfile change..
for i = 1 to 5
  select i
    case 1 * one() 
      debug "Case 1"
    case 2 * one()
      debug "Case 2"
    case 3 * one()
      debug "Case 3"
    case 4 * one()
      debug "Case 4"
    case 5 * one()
      debug "Case 5"
    default
      ;trap an error
      debug "Logic error..."
;      end
  endSelect
next i

end
Dr_Wildrick
User
User
Posts: 36
Joined: Fri Feb 23, 2007 8:00 pm
Location: New York

The flaw in your code

Post by Dr_Wildrick »

Yes, you have created a vernam *TYPE* of cypher but it can be cracked in seconds. The flaw is not in your theory - it is in your random number generator. If you can recreate the same randonm "pad" to decrypt it - so can anyone else with little effort. When the security of the Netscape browser was cracked - it was not the strong encryption they attacked - they simply reproduced the same series of psuedo random numbers. A computer remeber is inherently NON-random. Many attempts t make random number generators have been done in the past relying on user input, sound card input - all can be cracked in seconds even if you us a mouse's movement for your random seeds. For a decent psuedo and cryptographicly secure number generator, you may want to try the yarow generator. But that would defeat the intention of a long key as you would have to store the key because a one time pad is just that - one that can be generated randomly just once. Remove that factor and you no long have proof of un crackability and it can be demonstrated by any serious programmer in seconds.
Please do not take that as a criticism, cryptography is about learning from others, refining your idea and trying again. Your off to a good start. And your theory is sound, it the implementation that is weak. Good luck with your research.
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Post by utopiomania »

I wrote:
Note that my procedures in fact are using a random key as long as the message, but that the random
sequences are initiated in 'both ends' by the same random seeds.
Thanks for your feedback. As you can see from the quote above, I'm aware of the problem you point out.

In fact this particular effort sucks, baaaadly. :oops: :wink:
Dr_Wildrick
User
User
Posts: 36
Joined: Fri Feb 23, 2007 8:00 pm
Location: New York

Allrady aware

Post by Dr_Wildrick »

Ah, my apologies, you must forgive. English is my 4th language and my worst.
Kind regards,
Dr. Wildrick
citystate
Enthusiast
Enthusiast
Posts: 638
Joined: Sun Feb 12, 2006 10:06 pm

Post by citystate »

with your original code, what exactly is the difference between encode() and decode()? wouldn't it work just as well if you amalgamated the two procedures?

Code: Select all

global pos, lim 

procedure endecode(*mem) 
  if pos <= lim 
    pokeC(*mem + pos, peekC(*mem + pos) ! random(255)) 
    pos + 1 
    endecode(*mem) 
  else 
    procedureReturn 
  endIf 
endProcedure 

;run 
what.s = "USERNAME USERNAME USERNAME USERNAME USERNAME USERNAME" 
lim = len(what) 

*mem = allocateMemory(lim) 
*ptr = *mem 
copyMemoryString(@what, @*ptr) 

debug "Entered into protector:" 
debug what 

randomSeed(128) 
endecode(*mem) 
debug "written into placeholder:" 
debug peekS(*mem) 

pos = 0 
randomSeed(128) 
endecode(*mem) 
debug "Displayed in aboutbox:" 
debug peekS(*mem) 

end
I haven't tried it out myself (I'm at work) but am pretty sure the above will work in the same way as your original code

Cheers,
Matt
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Post by Froggerprogger »

some cents on that:
What you have is an encryption scheme based on a PRG (pseudorandomnumber-generator). (There en- and decryption are the same.) you have 8bit blocksize and you use a fixed key (the random seed). So your security bases on the fact that the attacker doesn't know how your encryption scheme works, what is very bad. Any encryption scheme should follow Kerckhoff's principle, that is, all about the encryption scheme is public (all but the currently used secret key), so you have to assume that an attacker exactly knows how your encryption works. In your current version an attacker simply has to run your public known method decrypt and is able to decrypt, because you use a fixed number for randomseed.

So you should parametrize the used key/random seed i.e. by a password that is translated to a number between 1 and $7FFFFFFF, so your highest possible keylength would be 32bit which is ok for some easy applications.

If you have done so far, you will achieve a PRG-based encryption with a keylength of 32 bit. Some disadvantages on that:
- the PRG you are based on (PB's Random-function) is supposed to be a linear congruency randomizer, which is much more easy to break that others
- the PRG-based encryption is not secure against little further advanced attackers that know a little bit more than simply eavesdropping the encrypted message. Imagine (plaintext recovery under known plaintext attacks): The attacker knows any pair of encryption AND plaintext. Than by a simple XOR he will get the PR-Numbers created by the password. Whenever you use the same password for another encryption, the attacker can easily decrypt the cipher it by simple XORing that numbers with your cipher.

Good encryption-schemes offer more security to even much higher capabilities of an attacker and less goals and therefore have at least to be randomized, so that the encryption of the same message with the same key leads to totally different messages. (Possible to achieve by adding one random-byte to each encryption-block which is involved in the encyrption)
%1>>1+1*1/1-1!1|1&1<<$1=1
Post Reply