MD5 + Modified RC4 stream chiper!

Share your advanced PureBasic knowledge/code with the community.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

MD5 + Modified RC4 stream chiper!

Post by Rescator »

This is a modification of the RC4mem() routine made by Pille (and later improved by Friedhelm)

Explanation and misc babble is in the source commentary.
Since this routine is so heavily based on Pille and Friedhelm's work I decided
to share it right back with the great and talented people on this board.

Oh and if anyone see any mistakes/bugs, by all means correct me.
There's always something to learn! :)

And as a extra bonus, there are two hex routines that may be useful, for example with storage of passwords on disk/config files. (i.e autologin etc)

Code: Select all

EnableExplicit

;EmSai Compressed Archive MD5 RC4 Enhanced Chiper (ECAMREC) or ECA-MD5-RC4-E
;This one is used as the main encryptiton for EmSai Compressed Archives (.ECA)
Procedure.l ECAMREC(m.l,l.l,Key.s,mode.l)
;ECAMREC(*MemoryBuffer.l, MemeryLength.l, Key.s, Mode.l)
;If mode is 0 then encryption is skipped.
;if mode is 1 then ECAMREC mode is used.
;If Mode is 2 then classic RC4 is used.
Static i.l,j.l,x.l,z.l,Y.l,t.l,e.s,*mm.BYTE,Dim rcS.w(255), Dim rcK.w(255)
Static text$,l2.l
 If mode=0 : ProcedureReturn m : EndIf
 ; the start of the ECAMREC chiper init
 If Key ;if pass/key is anything else than "" then reset the chiper stream
  If mode=1
   e=""
   l2=StringByteLength(Key,#PB_UTF8)
   z=l2
   text$=Space(l2)
   PokeS(@text$,Key,#PB_Any,#PB_UTF8)
   For i=0 To (l2-1) Step 2
    e=e+MD5Fingerprint(@text$+i,z)
    z-2
   Next i
  Else
   e=Key
  EndIf
  l2=StringByteLength(e,#PB_UTF8)
  text$=Space(l2)
  PokeS(@text$,e,#PB_Any,#PB_UTF8)
  ;Most passwords "people" come up with is less than 16 bytes
  ;bummer is if they use >16 char pass, as this encryption truncates at 16 bytes.
  ;The md5+modified rc4 combo ensure that only brute force/dictionary attacks are possible.
  ;brute force would be very time consuming, and if the user ain't dumb, then a dictionary
  ;attack would be equally painfull, esp. since the attacker has no clue how long the password
  ;originaly is, it could be 1 char or it could be >16, only easy passwords can be guessed.
  ;Add to this that with odd length passwords the last char is treated as a pair too.
  ;The md5fingerprint method above is odd, as it works on steps of two inversively.
  ;Thus the first md5fingerprint is of the entire string,
  ;the second is of the entire string minus the 2 first and last 2 chars of the string.
  ;etc etc, until the last md5fingerprint which is of the middle two chars,
  ;and if the password is a odd length then it could just be a md5fingerprint of a single char.
  ;A 1 char password would be 32bytes (256bit) a 16char one would be 2048bits.
  ;The average password length people use is usually around 8 bytes.
  ;Which would mean the above code would geerate a 1024bit one.
  ;Meaning generally speaking this modified RC4 uses 256-2048bit keys.
  ;It also permutates (that the right word or is it iterate?)
  ;the first 251 cycles/passes, since I read somewhere that RC4 is that strong
  ;at the start (first 100 or so chars) and picks up strength a bit later
  ;So I decided to just dump the first 200+ iterations,
  ;I chose 251 iterations as a sorta joke, as 251 is a prime,
  ;and we all know how crazy encryption people are about prime numbers hehe!

  j=0
  z.l=l2-1
  For i=0 To 255
    rcS(i)=i
    If j>z
     j=0
    EndIf
    rcK(i)=PeekB(@text$+j) & 255
    j+1
  Next i

  j=0
  For i=0 To 255
   j=(j+rcS(i)+rcK(i)) & 255
   z=rcS(i)
   rcS(i)=rcS(j)
   rcS(j)=z
  Next i

  i=0 ;these two are very important as they actually start the chiper stream
  j=0 ;the main loop below this loop permutates the stream using these two values
  ; permutate(!) the chiper stream by not "using" the 256 first values
  If mode=1 ; if ECAMREC mode, if rc4 classic this part is skipped
   For x=0 To 250 ;do 0-250 runs, i.e 251 (which is a prime :)
    i=(i+1) & 255
    j=(j+rcS(i)) & 255
    Swap rcS(i),rcS(j)
   Next
  EndIf
  ;Now i and j has sorta been "seeded" if you can call it that.
 EndIf ; end of the ECAMREC chiper init, below is the actual chiper loop

 ;This part is rather interesting.
 ;Only the first block od data to be encrypted should be called with a password
 ;following blocks of the data or file should have a blank "" password key.
 ;This allows this chiper to shows it's full potential since RC4 is a stream chiper
 ;if a file is small and fits in your buffer then you prolly just need to call this routine once
 ;however large files are usually always handled in blocks/buffers,
 ;and the other RC4 routines around are not made to support that
 ;which is silly as RC4 is a stream chiper, not a block chiper
 ;RC4 is weaker if used in blocks (since the encryption would repeat),
 ;however this routine use RC4's full strength as a stream chiper.
 *mm=m
 For x=0 To l-1
  i=(i+1) & 255
  j=(j+rcS(i)) & 255
  z=rcS(i)
  rcS(i)=rcS(j)
  rcS(j)=z
  t=(rcS(i)+(rcS(j) & 255)) & 255
  Y=rcS(t)
  *mm\b!Y
  *mm+1
 Next
ProcedureReturn m
EndProcedure

Procedure.s ECAMREC_To_Hex(rc4String.s,rc4Key.s)
Protected Encrypted.s, len.l, Hex.s, Text.s, x
len=StringByteLength(rc4String,#PB_UTF8)
Encrypted=Space(len)
PokeS(@Encrypted,rc4String,#PB_Any,#PB_UTF8)
ECAMREC(@Encrypted,len,rc4Key,1)
For x=0 To len-1
 Text=Text+RSet(Hex(PeekB(@Encrypted+x) & $FF),2,"0")
Next
ProcedureReturn Text
EndProcedure

Procedure.s ECAMREC_From_Hex(rc4String.s,rc4Key.s)
Protected len.l, Hex.s, Text.s, x, a, b, c, Encrypted.s,z.l
Hex.s=UCase(rc4String.s)
len.l=Len(Hex)*SizeOf(Character)
Encrypted=Space(Len(Hex))
z=0
For x=0 To len-SizeOf(Character) Step SizeOf(Character)*2
 a=PeekC(@Hex+x) & $FF
 If a<58
  a=a-48
 Else
  a=a-55
 EndIf
 b=PeekC(@Hex+x+SizeOf(Character)) & $FF
 If b<58
  b=b-48
 Else
  b=b-55
 EndIf
 PokeB(@Encrypted+z,(a<<4)+b)
 z+1
Next
ECAMREC(@Encrypted,z,rc4Key,1)
Text=PeekS(@Encrypted,z,#PB_UTF8)
ProcedureReturn Text.s
EndProcedure

Define text$

text$="Testing this!"
Debug text$

text$=ECAMREC_To_Hex(text$,"secret")
Debug text$

text$=ECAMREC_From_Hex(text$,"secret")
Debug text$
Edit: Fixed a typo, the code should now work!
Edit: Code now compiles with EnableExplicit, arrays also made static.
Edit: Uses Swap in loops (thanks to mskuma for the tip)
Edit: Unicode supported.
Last edited by Rescator on Sun Sep 14, 2008 9:26 pm, edited 6 times in total.
Num3
PureBasic Expert
PureBasic Expert
Posts: 2812
Joined: Fri Apr 25, 2003 4:51 pm
Location: Portugal, Lisbon
Contact:

Post by Num3 »

Interesting concept!

I like it :D
mskuma
Enthusiast
Enthusiast
Posts: 573
Joined: Sat Dec 03, 2005 1:31 am
Location: Australia

Re: MD5 + Modified RC4 stream chiper!

Post by mskuma »

This is great Rescator - thanks very much.
Rescator wrote:

Code: Select all

 ;This part is rather interesting.
 ;Only the first block od data to be encrypted should be called with a password
 ;following blocks of the data or file should have a blank "" password key.
I think for that to work properly, it is necessary to add the Static keyword to the line:

Code: Select all

Dim rcS.w(255) : Dim rcK.w(255)
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Thanks mskuma edited the post now, it's now fully PB4'ified.
(think I wrote that for PB 4.x actually at first)
Also added your Swap tip to the two loops, thanks :)
User avatar
thyphoon
Enthusiast
Enthusiast
Posts: 345
Joined: Sat Dec 25, 2004 2:37 pm

Post by thyphoon »

Your code is very exelent !
But how can use it to crypt unicode text ?
it don't work with unicode !

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

Post by Rescator »

Updated the source above, unicode fully supported.

The way I did it (and I advise others to handle stuff like this) was to convert the text to UTF8 then do normal byte related stuff.
And then the reverse, convert the text from UTF8.

The updated source seems compatible with the old,
and provided no really weird characters (that would require multiple byte encoding under UTF8) was used as the key this source should be able to decrypt the data the old source encrypted as well.

No optimizations have been done, I only fixed it so it would work with unicode (it still works in Ascii mode as well).
User avatar
thyphoon
Enthusiast
Enthusiast
Posts: 345
Joined: Sat Dec 25, 2004 2:37 pm

Post by thyphoon »

Thanks :D it's great !! :D
Post Reply