Jump to content


Photo

RC4 encryption to hex stream


  • Please log in to reply
36 replies to this topic

#1 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 08 December 2005 - 11:11 PM

There are problems with RC4 in binary mode, as Rajat posted: the encrypted text can contain non-printable characters. If you write it in a file and read back, different characters could map to the same one, others just get lost. An occasional NUL character is treated as a string terminator. You have to convert the ciphertext, for example, to a stream of hex digits and convert it back to binary before decryption. Still there are difficulties, like the length of a binary buffer has to be kept in a separate variable. To make life easier, here is a variant of Rajat's RC4 script. RC4txt2hex takes a text string, encrypts it with a pass phrase and returns the result as a stream of hex digits. RC4hex2txt does the opposite, allowing the decryption of a hex stream created beforehand. The hex string can be freely manipulated with the standard functions of AHK or written to a text file or edit with any text editor, there are no problems with special characters.

Of course you can use AES or TEA, the Tiny Encryption Algorithm, instead: this, or that. It is considered more secure, but for personal use there should be no security concerns with either of them.
Process Priority,,High                 ; Run faster
SetBatchLines -1

RC4Pass = Version 1.0.40.09`nwww.autohotkey.com`n©2003-2005 Chris Mallett, portions ©AutoIt Team

RC4Data = AutoHotkey unleashes the full potential of your keyboard, joystick, and mouse. For example, in addition to the typical Control, Alt, and Shift modifiers, you can use the Windows key and the Capslock key as modifiers.

RC4Enc := RC4txt2hex(RC4Data "`n" RC4Data "`n" RC4Data "`n" RC4Data "`n" RC4Data,RC4Pass)
RC4Dec := RC4hex2txt(RC4Enc,RC4Pass)
MsgBox %RC4Data%`n`nEncrypted 5-times with pass:`n`n%RC4Pass%`n`nto`n`n%RC4Enc%`n`nDecypted to`n`n%RC4Dec%

ExitApp

RC4txt2hex(Data,Pass) {
   Format := A_FormatInteger
   SetFormat Integer, Hex
   b := 0, j := 0
   VarSetCapacity(Result,StrLen(Data)*2)
   Loop 256
      a := A_Index - 1
     ,Key%a% := Asc(SubStr(Pass, Mod(a,StrLen(Pass))+1, 1))
     ,sBox%a% := a
   Loop 256
      a := A_Index - 1
     ,b := b + sBox%a% + Key%a%  & 255
     ,sBox%a% := (sBox%b%+0, sBox%b% := sBox%a%) ; SWAP(a,b)
   Loop Parse, Data
      i := A_Index & 255
     ,j := sBox%i% + j  & 255
     ,k := sBox%i% + sBox%j%  & 255
     ,sBox%i% := (sBox%j%+0, sBox%j% := sBox%i%) ; SWAP(i,j)
     ,Result .= SubStr(Asc(A_LoopField)^sBox%k%, -1, 2)
   StringReplace Result, Result, x, 0, All
   SetFormat Integer, %Format%
   Return Result
}

RC4hex2txt(Data,Pass) {
   b := 0, j := 0, x := "0x"
   VarSetCapacity(Result,StrLen(Data)//2)
   Loop 256
      a := A_Index - 1
     ,Key%a% := Asc(SubStr(Pass, Mod(a,StrLen(Pass))+1, 1))
     ,sBox%a% := a
   Loop 256
      a := A_Index - 1
     ,b := b + sBox%a% + Key%a%  & 255
     ,sBox%a% := (sBox%b%+0, sBox%b% := sBox%a%) ; SWAP(a,b)
   Loop % StrLen(Data)//2
      i := A_Index  & 255
     ,j := sBox%i% + j  & 255
     ,k := sBox%i% + sBox%j%  & 255
     ,sBox%i% := (sBox%j%+0, sBox%j% := sBox%i%) ; SWAP(i,j)
     ,Result .= Chr((x . SubStr(Data,2*A_Index-1,2)) ^ sBox%k%)
   Return Result
}

Edit: 20061219 - New version for AutoHotKey Version 1.0.46.00
Edit: 20061220 - AutoTrim handling is not needed in the new AHK versions, removed (thanks PhiLho)
Edit: 20061221 - Removed dummy Abs()
Edit: 20100420 - Removed old versions, added missing swaps (based on the 1-liner of temp01) and used continuation lines for a slight speedup

#2 SKAN

SKAN
  • Administrators
  • 9062 posts

Posted 14 July 2006 - 07:47 AM

Dear Laszlo, :)

I wanted to store my Username+Password for my multiple GMail accounts as a single encrypted file.
I tried your RC4 functions, and

It is working great!!!

Thank you very much. :)

Regards, :)

#3 Atomhrt

Atomhrt
  • Members
  • 124 posts

Posted 14 July 2006 - 03:48 PM

This is nice, thanks! A thought...

It would be nice to hide the length of the input string by generating a output hex string that is actually longer than the input string. One way to do this is have a byte in the output string that contains the actual size of the input sting in the output string.

#4 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 14 July 2006 - 04:10 PM

Do you mean, that the public length of the password leaks? It is true, but it is not a concern with good enough passwords. You should never use shorter than 8 characters, with shifts, symbols, etc. There are 95^8 = ~6.6/10^15 candidates to try for a blind key search. If the attacker does not know the length, he has to try all the shorter passwords, too, 1+95+95^2...+95^8 = ~6.7*10^15 possibilities, practically the same.

Of course, if the password is chosen poorly, a dictionary attack might succeed, regardless of the encryption method.

#5 SKAN

SKAN
  • Administrators
  • 9062 posts

Posted 19 December 2006 - 01:51 PM

Dear Laszlo, :)

I have been using the code without even taking a look at it ( all these days. )
Someday, I would like to study this code and so, would like the title post to be intact...

However, with SubStr() & Comma Seperated Values (and maybe RegEx), can this code be shortened and increased in performance ?

Regards, :)

#6 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 19 December 2006 - 08:35 PM

with SubStr() & Comma Seperated Values (and maybe RegEx), can this code be shortened and increased in performance ?

I added a new version in the first post. It is shorter and should be somewhat faster. I did not put long assignments into one line, to maintain legibility, but you could.

#7 SKAN

SKAN
  • Administrators
  • 9062 posts

Posted 20 December 2006 - 05:49 AM

I added a new version in the first post. It is shorter and should be somewhat faster.


Thank you very much, Sir. :D

I did not put long assignments into one line, to maintain legibility, but you could.


The algorithm is already hard for me to grasp. Once understood, the number of lines is what I would try to reduce.
I will have to study and compare both the versions side by side.

These pair of functions are among the finest available in the forum...

Thanks again and Regards, :)

#8 PhiLho

PhiLho
  • Fellows
  • 6850 posts

Posted 20 December 2006 - 03:42 PM

With the changes in AutoHotKey Version 1.0.46.00, some speedup and simplifications are possible:

Impressive. I played a bit with the code, never able to make it faster... ;-)
It shows:
1) Your code is highly optimized. Nothing new here...
2) Chris' code is well optimized too, SubStr is fast!

Two minor remarks:
- I always found strange that & has a lower priority than + (even the C language makers stated it was a design error), so I tend to write: b := (b + sBox%a% + Key%a%) & 255
Of course, you code (and style) is valid.
- In RC4hex2txt, you don't have to play with AutoTrim, since you only use expression concatenation.

#9 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 20 December 2006 - 04:02 PM

you don't have to play with AutoTrim, since you only use expression concatenation.

Thanks, I removed it. It just remained there from earlier versions, when it was necessary.

#10 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 20 December 2006 - 04:38 PM

code is highly optimized

There are still some speedups possible. For example, when we compute k, the sum sBox%i% + sBox%j% cannot be larger than 510 (255+255), so if we duplicate the sBox array, this larger index still finds the right entry. This way we can remove the mask operation (& 255). It means a couple of percents acceleration for longer texts. (At short texts it is offset by the need for copying 255 array elements. I did not copy this to the first post, because in typical applications it does not brings you much.)
RC4txt2hex(Data,Pass) {

   Format := A_FormatInteger

   SetFormat Integer, Hex

   b := 0, j := 0

   VarSetCapacity(Result,StrLen(Data)*2)

   Loop 256 {

      a := A_Index - 1

      Key%a% := Asc(SubStr(Pass, Mod(a,StrLen(Pass))+1, 1))

      sBox%a% := a

   }

   Loop 256 {

      a := A_Index - 1

      b := b + sBox%a% + Key%a%  & 255

      T := sBox%a%

      sBox%a% := sBox%b%

      sBox%b% := T

   }

   Loop 255 {

      i := A_Index - 1

      k := i + 256

      sBox%k% := sBox%i%

   }

   Loop Parse, Data

   {

      i := A_Index & 255

      j := sBox%i% + j  & 255

      k := sBox%i% + sBox%j%

      Result .= SubStr(Asc(A_LoopField)^sBox%k%, -1, 2)

   }

   StringReplace Result, Result, x, 0, All

   SetFormat Integer, %Format%

   Return Result

}



RC4hex2txt(Data,Pass) {

   b := 0, j := 0

   VarSetCapacity(Result,StrLen(Data)//2)

   Loop 256 {

      a := A_Index - 1

      Key%a% := Asc(SubStr(Pass, Mod(a,StrLen(Pass))+1, 1))

      sBox%a% := a

   }

   Loop 256 {

      a := A_Index - 1

      b := b + sBox%a% + Key%a%  & 255

      T := sBox%a%

      sBox%a% := sBox%b%

      sBox%b% := T

   }

   Loop 255 {

      i := A_Index - 1

      k := i + 256

      sBox%k% := sBox%i%

   }

   Loop % StrLen(Data)//2 {

      i := A_Index  & 255

      j := sBox%i% + j  & 255

      k := sBox%i% + sBox%j%

      Result .= Chr(Abs("0x" SubStr(Data,2*A_Index-1,2)) ^ sBox%k%)

   }

   Return Result

}


#11 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 21 December 2006 - 07:23 PM

...to get a fraction of a percent speedup, we can exploit a peculiarity of AHK v 1.0.46.xx: expressions containing literal strings are evaluated less deeply than the ones with variables. In the function RC4hex2txt we need to prepend "0x" to the pair of hex digits processed. If we do it the obvious way: ("0x" . SubStr(Data,2*i-1,2)), we get a string, which cannot be part of an arithmetic expression. There are several workarounds, like passing this expression to a function, which does not change its numerical value, like Abs(), or Ceil(). We can force the evaluation with a dummy assignment, like (x := "0x" . SubStr(Data,2*i-1,2)), but the simplest and fastest (so far) solution is to store the string "0x" in the variable x, and use (x . SubStr(Data,2*A_Index-1,2)). Interestingly, AHK evaluates this expression numerically.
RC4hex2txt(Data,Pass) {
   b := 0, j := 0, x := "0x"
   VarSetCapacity(Result,StrLen(Data)//2)
   Loop 256 {
      a := A_Index - 1
      Key%a% := Asc(SubStr(Pass, Mod(a,StrLen(Pass))+1, 1))
      sBox%a% := a
   }
   Loop 256 {
      a := A_Index - 1
      b := b + sBox%a% + Key%a%  & 255
      T := sBox%a%
      sBox%a% := sBox%b%
      sBox%b% := T
   }
   Loop % StrLen(Data)//2 {
      i := A_Index  & 255
      j := sBox%i% + j  & 255
      k := sBox%i% + sBox%j%  & 255
      Result .= Chr((x . SubStr(Data,2*A_Index-1,2)) ^ sBox%k%)
   }
   Return Result
}
Since it has no drawback, I updated the first post with this trick.

#12 iason

iason
  • Members
  • 135 posts

Posted 30 September 2007 - 03:16 PM

Laszlo, thank You very much Sir again for another bit of very useful code!

A very straight forward question:
Is this safe to use in (my) bilingual system? (I want to hide information in .ini files, hidden from prying eyes.)

#13 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 30 September 2007 - 03:32 PM

If the input does not contain NUL characters (does not normally happen), it is safe in any langauge.

#14 iason

iason
  • Members
  • 135 posts

Posted 30 September 2007 - 06:54 PM

...(does not normally happen)...

This declaration eases my worry, since being clueless i don't well know what a NUL character is - searched Wiki etc. and did not understand much about NUL/NULL chars etc, i just suspect and hope that they are are absent in ordinary strings such as mine.

Thanks again!

#15 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 30 September 2007 - 06:58 PM

Yes, they are not in ANSI strings, only in binary data.