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.