MD5 + Modified RC4 stream chiper!
Posted: Sat Feb 19, 2005 5:21 pm
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)
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.
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: Code now compiles with EnableExplicit, arrays also made static.
Edit: Uses Swap in loops (thanks to mskuma for the tip)
Edit: Unicode supported.