Jump to content

CipherSaber-2 - RC4 based stream encryption


wraithdu
 Share

Recommended Posts

This is my implementation of CipherSaber-2 ( http://ciphersaber.gurus.org/ ). CipherSaber is a version of RC4 that adds a 10 byte random IV to each piece of encoded data. CipherSaber-2 augments this with a configurable number of key setup rounds ( http://ciphersaber.gurus.org/faq.html#cs2 ).

I decided to do the actual encryption / decryption in machine code so it is fast enough to be used on moderately sized files. The key setup is still done in AutoIt.

I prefer this routine to AutoIt's _StringEncrypt because the output is the same size as the input, regardless of key rounds or encryption rounds.

Note: If you visit the site to learn more or run the official test cases, a bunch of the links incorrectly point to a .com domain. Just change those to .org and they will work properly.

#include-once
#include <Memory.au3>

OnAutoItExitRegister("__CS_Cleanup")

Global $__g_CS_pCode = 0

; #FUNCTION# ====================================================================================================================
; Name ..........: _CipherSaber
; Description ...: Encrypt / decrypt a chunk of binary data.
; Syntax ........: _CipherSaber( $bData, $sKey [, $iKeyRounds = 20 [, $doEncrypt = True ]] )
; Parameters ....: $bData               - Chunk of binary data.
;                  $sKey                - User key, string <= 246 characters, will be truncated if too long.
;                  $iKeyRounds          - [optional] Number of rounds during key setup. Default is 20.
;                  $doEncrypt           - [optional] Whether to encrypt or decrypt. Default is True.
; Return values .: The encrypted / decrypted binary data.
; Author ........: Erik Pilsits
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........: http://ciphersaber.gurus.org/
; Example .......: No
; ===============================================================================================================================
Func _CipherSaber($bData, $sKey, $iKeyRounds = 20, $doEncrypt = True)
    Local $IV = Default
    If Not $doEncrypt Then $IV = BinaryMid($bData, 1, 10)
    ;
    ; RC4 has max key length of 256 bytes
    ; user key has max length of 246 bytes to allow for 10 bytes of random IV
    Local $state = __CS_KeySetup(StringLeft($sKey, 246), $IV, $iKeyRounds)
    Local $sOut = Binary("")
    If $doEncrypt Then
        $sOut = $IV
    Else
        $bData = BinaryMid($bData, 11)
    EndIf
    ;
    Return $sOut & __CS_Cipher($bData, $state)
EndFunc   ;==>_CipherSaber

; #FUNCTION# ====================================================================================================================
; Name ..........: _CipherSaber_File
; Description ...: Encrypt / decrypt a file.
; Syntax ........: _CipherSaber_File( $fileIn, $fileOut, $sKey [, $iKeyRounds = 20 [, $doEncrypt = True ]] )
; Parameters ....: $fileIn              - Path to the input file.
;                  $fileOut             - Path to the output file.
;                  $sKey                - User key, string <= 246 characters, will be truncated if too long.
;                  $iKeyRounds          - [optional] Number of rounds during key setup. Default is 20.
;                  $doEncrypt           - [optional] Whether to encrypt or decrypt. Default is True.
; Return values .: Success              - 1
;                  Failure              - 0 and sets @error
;                                       | 1 - Error opening input file for readin
;                                       | 2 - Error opening output file for writing
; Author ........: Erik Pilsits
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _CipherSaber_File($fileIn, $fileOut, $sKey, $iKeyRounds = 20, $doEncrypt = True)
    Local $hIn = FileOpen($fileIn, 16)
    If $hIn = -1 Then Return SetError(1, 0, 0)
    Local $hOut = FileOpen($fileOut, 2+8+16)
    If $hOut = -1 Then
        FileClose($hIn)
        Return SetError(2, 0, 0)
    EndIf
    ;
    Local $IV = Default
    If Not $doEncrypt Then $IV = FileRead($hIn, 10)
    ;
    Local $state = __CS_KeySetup(StringLeft($sKey, 246), $IV, $iKeyRounds)
    If $doEncrypt Then FileWrite($hOut, $IV)
    ;
    Local $bData
    While 1
        $bData = FileRead($hIn, 1024^2*2)
        If @error Then ExitLoop
        FileWrite($hOut, __CS_Cipher($bData, $state))
    WEnd
    ;
    FileClose($hIn)
    FileClose($hOut)
    ;
    Return 1
EndFunc   ;==>_CipherSaber_File

#region INTERNAL FUNCTIONS
Func __CS_KeySetup($sKey, ByRef $IV, $iKeyRounds)
    Local $key = StringToBinary($sKey)
    Local $i, $j, $n
    ;
    If $IV = Default Then
        ; encrypt
        ; create 10 bytes random data for IV
        $IV = ""
        For $i = 1 To 10
            $IV &= Hex(Random(0, 255, 1), 2)
        Next
        $IV = Binary("0x" & $IV)
    EndIf
    $key &= $IV
    ;
    ; key setup
    ; use struct for key+IV because they are faster than BinaryMid
    Local $state[256], $keylen = BinaryLen($key)
    Local $tKey = DllStructCreate("byte[" & $keylen & "]")
    DllStructSetData($tKey, 1, $key)
    ;
    For $i = 0 To 255
        $state[$i] = $i
    Next
    ;
    $j = 0
    For $k = 1 To $iKeyRounds
        For $i = 0 To 255
            $j = Mod($j + $state[$i] + DllStructGetData($tKey, 1, Mod($i, $keylen) + 1), 256)
            $n = $state[$i]
            $state[$i] = $state[$j]
            $state[$j] = $n
        Next
    Next
    ; create state struct from array
    Local $tState = DllStructCreate("byte[256]")
    For $i = 0 To 255
        DllStructSetData($tState, 1, $state[$i], $i+1)
    Next
    Return $tState
EndFunc   ;==>__CS_KeySetup

Func __CS_Cipher(Const ByRef $bData, ByRef $tState)
    ; initialize machine code
    If Not $__g_CS_pCode Then
        Local $bCode
        If @AutoItX64 Then
            $bCode = Binary("0x5557565385D27E508D42FF4531C931ED488D7401018D55010FB6EA4863C54C01C00FB618440FB6D34501D14181E1FF0000004D63D94D01C3410FB613881041881B440210450FB6D2430FB6041030014883C1014839F175BD5B5E5F5DC3")
        Else
            $bCode = Binary("0x5557565383EC0C8B5424248B4C242885D27E598B44242031DB31ED891C2401C2895424088D55010FB6EA8D14290FB61A0FB6F3885C24078B1C2401F381E3FF00000089DF01CF891C240FB61F881A0FB65C2407881F0FB61201F20FB6F20FB61431301083C0013B44240875B883C40C5B5E5F5DC20C00")
        EndIf
        $__g_CS_pCode = _MemVirtualAlloc(0, BinaryLen($bCode), $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
        DllStructSetData(DllStructCreate("byte[" & BinaryLen($bCode) & "]", $__g_CS_pCode), 1, $bCode)
    EndIf
    ; set data to struct
    Local $i = 0, $j = 0, $n, $len = BinaryLen($bData)
    Local $tData = DllStructCreate("byte[" & $len & "]")
    DllStructSetData($tData, 1, $bData)
    ; call cipher
    DllCallAddress("none", $__g_CS_pCode, "struct*", $tData, "int", $len, "struct*", $tState)
    ;
;~     For $k = 1 To $len
;~         $i = Mod($i + 1, 256)
;~         $j = Mod($j + $state[$i], 256)
;~         $n = $state[$i]
;~         $state[$i] = $state[$j]
;~         $state[$j] = $n
;~         $n = Mod($state[$i] + $state[$j], 256)
;~         DllStructSetData($tData, 1, BitXOR($state[$n], Int(DllStructGetData($tData, 1, $k))), $k)
;~     Next
    ;
    Return DllStructGetData($tData, 1)
EndFunc   ;==>__CS_Cipher

Func __CS_Cleanup()
    If $__g_CS_pCode Then _MemVirtualFree($__g_CS_pCode, 0, $MEM_RELEASE)
EndFunc   ;==>__CS_Cleanup
#endregion INTERNAL FUNCTIONS

cipher.c

void __stdcall cipher(unsigned char src[], int srcSize, unsigned char state[256]) {
    int c = 0, i = 0, j = 0, n = 0;
    for (c = 0; c < srcSize; c++) {
        i = (i + 1) % 256;
        j = (j + state[i]) % 256;
        n = state[i];
        state[i] = state[j];
        state[j] = n;
        src[c] = state[((state[i] + state[j]) % 256)] ^ src[c];
    }
}

Example

#include <_CipherSaber.au3>

; NOTE: the IV is random data, so the encrypted data is different each time

$p = "mypassword"

; string
$s = "Some string to encrypt"
$e = _CipherSaber(StringToBinary($s), $p)
ConsoleWrite("encrypted: " & $e & @CRLF)
; $e is already binary data, default number of key rounds is 20, set flag to decrypt
; returned decrypted data is also binary
$d = _CipherSaber($e, $p, 20, False)
ConsoleWrite("decrypted: " & BinaryToString($d) & @CRLF)

ConsoleWrite("---------------------" & @CRLF)

; file
$f = @DesktopDir & "\in.txt"
$o = @DesktopDir & "\enc.txt"
$o2 = @DesktopDir & "\dec.txt"
FileWrite($f, "Some sensitive file")
ConsoleWrite("original file: " & Binary(FileRead($f)) & @CRLF)
_CipherSaber_File($f, $o, $p)
ConsoleWrite("encrypted file: " & Binary(FileRead($o)) & @CRLF)
; default key rounds 20, flag to decrypt
_CipherSaber_File($o, $o2, $p, 20, False)
ConsoleWrite("decrypted file: " & Binary(FileRead($o2)) & @CRLF)
; cleanup
FileDelete($f)
FileDelete($o)
FileDelete($o2)
Link to comment
Share on other sites

  • 4 weeks later...

"In George Lucas' Star Wars trilogy, Jedi Knights were expected to make their own light sabers. The message was clear: a warrior confronted by a powerful empire bent on totalitarian control must be self-reliant. As we face a real threat of a ban on the distribution of strong cryptography, in the United States and possibly world-wide, we should emulate the Jedi masters by learning how to build strong cryptography programs all by ourselves. If this can be done, strong cryptography will become impossible to suppress."

Looks great wraithdu, but it's hardly building your own Light Saber! The main feature of Cipher Saber is, "it's designed to be simple enough that even novice programmers can memorize the algorithm and implement it from scratch". Relying on outside sources, like DLL files, simply defeats the purpose.

Try something like this, instead..

; CipherSaber stream-cipher
; Designed by Arnold Reinhold
;
; CipherSaber (1 and 2) encryption and decryption with base64 encoding
; For more info on CS algorithm go to http://ciphersaber.gurus.org/
; Based on Emilis Dambauskas' PHP script: http://www.phpclasses.org/browse/file/3689.html
; Which was based on Ian Gulliver's perl script: http://www.xs4all.nl/~cg/ciphersaber/ (DEAD)
;
; Pure AutoIt version by cor @ corz.org 2012 - Open Source
;

; base64 encrypt..
func CSEncrypt($str, $key, $csl=20)
$r_num = StringMid(MakeRandomXString(), 1, 10)
return _Base64Encode($r_num & AICipherSaber($str, $key, $r_num, $csl))
endfunc

; base64 decrypt..
func CSDecrypt($str, $key, $csl=20)
$str = BinaryToString(_Base64Decode($str))
$r_num = StringMid($str, 1, 10)
$str = StringMid($str, 11)
return AICipherSaber($str, $key, $r_num, $csl)
endfunc


; binary encrypt..
func CSbinEncrypt($str, $key, $csl=20)
$r_num = StringMid(MakeRandomXString(), 1, 10)
return $r_num & AICipherSaber($str, $key, $r_num, $csl)
endfunc

; binary decrypt..
func CSbinDecrypt($str, $key, $csl=20)
$str = BinaryToString($str)
$r_num = StringMid($str, 1, 10)
$str = StringMid($str, 11)
return AICipherSaber($str, $key, $r_num, $csl)
endfunc


; CipherSaber Algorithm..
func AICipherSaber($d, $p, $rnum, $csl=20)

$p &= $rnum
local $S[256]

for $i = 0 to 255
     $S[$i] = $i
next

local $j = 0, $t = StringLen($p)
local $kkk[256]

for $i = 0 to 255
     $kkk[$i] = Asc(StringMid($p, $j+1, 1))
     $j = Mod(($j + 1), $t)
next

$j = 0;
for $kk = 0 to $csl-1 ; CS2 loops
     for $i = 0 to 255
         $j = BitAND(($j + $S[$i] + $kkk[$i]), 0xff)
         $t = $S[$i]
         $S[$i] = $S[$j]
         $S[$j] = $t
     next
next
$i = 0
$j = 0
local $ii = 0
local $ret = ''
$dlen = StringLen($d)

for $ii = 0 to $dlen-1
     $c = StringMid($d, $ii+1, 1)
     $i = BitAND(($i + 1), 0xff)
     $j = BitAND(($j + $S[$i]), 0xff)
     $t = $S[$i]
     $S[$i] = $S[$j]
     $S[$j] = $t
     $t = BitAND(($S[$i] + $S[$j]), 0xff)
     $ret &= Chr(BitXOR($S[$t], Asc($c)))
next
return $ret
endfunc


; used internally..
; works great, but could be improved!
func MakeRandomXString()
SRandom(@MSEC*@MSEC*@MIN)
local $a[32]
local $x = 0
while $a[31] = ""
     $tmp = chr(Random(48, 71, 1))
     if StringIsXDigit($tmp) then
         $a[$x] = Asc($tmp)
         $x += 1
     endif
wend
return StringFromASCIIArray($a)
endfunc

If you want to use the Base64() versions, you will find plenty of implementations of Base64() kicking around the forums. I recommend the pure AutoIt versions by blindwig.

Here's a demo:

; decrypt a file..
$key = "MyKeyString"
$encrypted = FileRead("C:\path\to\encrypted.file")
FileWrite("C:\path\to\decrypted.file", CSbinDecrypt($encrypted, $key))

; basic variable encrpyt/decrpyt test:
$String = "Secret**FE*K**String"
$key = "me@myplace.org"

; Binary..
ConsoleWrite("BINARY: " & "->" & @LF)
$encrypted = CSbinEncrypt($String, $key)
ConsoleWrite("encrypted: " & "->" & BinaryToString(StringStripWS($encrypted, 8)) & @LF)
$decrypted = CSbinDecrypt($encrypted, $key)
ConsoleWrite("decrypted: " & "->" & $decrypted & "<-" & @LF)

; Base64..
ConsoleWrite(@LF)
ConsoleWrite("Base64: " & "->" & @LF)
$encrypted = CSEncrypt($String, $key)
ConsoleWrite("encrypted: " & "->" & $encrypted & @LF)
$decrypted = CSDecrypt($encrypted, $key)
ConsoleWrite(@LF)
ConsoleWrite("decrypted: " & "->" & $decrypted & "<-" & @LF)

You can grab an include with everything ready-to-go (and without the formatting destroyed!), here.

Have fun!

;o) Cor

Edited by corz

nothing is foolproof to the sufficiently talented fool..

Link to comment
Share on other sites

^^ I'm sure wraithdu would have written it that way if he'd wanted to. But the thing is he obviously can see limitation of the language he uses. That's why for critical parts of the code he used special technique to go around AutoIt's execution speed issues. That can be classified as rational action by individual with ability to think outside the box.

BTW, his code is self-sufficient, just as yours is, with no purpose defeated.

Edited by trancexx
Link to comment
Share on other sites

I can't add much to what trancexx has said, as usual, she's spot on.

But you can hardly say I'm 'relying' on something I wrote myself. I'm using machine code because it's super fast for the intensive parts of this algorithm. Even you cautioned people on your site to 'Have fun, but don't try it on any HUGE files!'. Well, while I wouldn't use this to encrypt gigabytes worth of data, you can certainly use my version to encrypt megabytes at a reasonable rate.

I wrote everything originally in AutoIt, and the AutoIt source for the machine code is there in the comment. Feel free to swap it out if you want to be masochistically purist, but I wouldn't recommend using it for files at that point.

Link to comment
Share on other sites

May I point out that all this is now completely irrelevant in today's world and 10-years outdated?

Recommendations that a secret symmetric key be changed every 1000 or so uses is a strong hint that it's fairly broken in the first place, not to mention that PGP is the standard way to exchange keys, or that it lacks almost every basic feature a strong encryption program needs for daily use by laymen.

Implementing such simple bare-bones ciphers is at most a very good learning exercise but their widespread use can prove dangerous if non-tech people blindly trust them to be safe in their routine use cases.

I'm merely expressing my opinion, not trying to anyhow denigrate work done by posters.

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

I'll leave it the authors of RC4 and CS-2 to defend the strength of their ciphers (CS-2 gets around the Arcfour / WEP vulnerability by introducing a custom number of key setup rounds, FYI, so it is not necessary to change keys every 1000 messages). I think it is a worthy replacement for AutoIt's _StringEncrypt, which is what I use it for, especially since _StringEncrypt increases the length of the message every additional round.

Edited by wraithdu
Link to comment
Share on other sites

I didn't talk about the strength or weakness of the underlying algorithm. _StringEncrypt is very dangerous and replacing it with something stronger is good.

What I questionned is that people may be misled (I don't mean by you) into thinking that such techniques can solve all their secrecy issues.

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...