String Encryption (encoding)

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: String Encryption (encoding)

#16 Post by Magialisk » 30 Jul 2013 16:01

I was bored this afternoon so I decided to make several improvements to my function library. First, in my encMono/decMono and encPoly/decPoly ciphers I rewrote the logic as FOR loops instead of GOTOs. Other than having to tweak around some messiness with characters that FOR didn't like (ie: space, comma, ;, *, ?) everything still worked. After this step the ciphers were running around 7-8% faster

After that I decided to take a big step and download dbenham's macroLib library. I'd been putting this off for a long time but I really wanted to dig in and start getting familiar with macros, so I went for it. The further along I get with my function library, the uglier it's going to be to start integrating/converting macros, right?

First I swapped the function calls from my own strLen, Upper, Lower, etc. function to use his library macros instead. Then I created new CharToNum and NumToChar macros in macroLib_String and replaced those calls as well. All told, everything still works and the above two ciphers are running at least 15-20% faster. If I have time in the next couple days I plan to go through Vigenere and Enigma and convert all the function calls to macro calls, then if I get really ambitious I'd like to try to convert my PRNG function to a macro itself, maybe stick it in macroLib_Numbers?

Great thanks to dbenham for the incredible macro library! I can't believe I avoided experimenting with your work for so long! :)

Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: String Encryption (encoding)

#17 Post by Magialisk » 02 Aug 2013 06:14

Well I've rewritten my "simple" functions as macros and added them to dbenham's macro libraries. Little did I realize he already had a random macro in macroLib_Num, but it simply calls %random% and can't be seeded, so I wrote my own version and dropped it in the library:

Code: Select all

set macro\args.RandomSeed=  MinVal  MaxVal  SeedVar  [RtnVar]
set macro\help.RandomSeed=  MinVal  MaxVal  SeedVar  [RtnVar]%\n%
  %\n%
  Compute a pseudo random integer value between numeric values%\n%
  MinVal and MaxVal. Uses a supplied SeedVar to bias the output.%\n%
  %\n%
  Sets SeedVar = next seed in this sequence%\n%
  Sets RtnVar = result%\n%
  or displays result if RtnVar not specified%\n%
  %\n%
  MinVal and MaxVal may be specified using any expression supported%\n%
  by SET /A%xLF%
set macro.RandomSeed=do (%\n%
  setlocal enableDelayedExpansion%\n%
  set /a "seed=!%%~c!*1103515245+12345"%\n%
  set /a "macro.Random.rtn=(!seed!/65536)%%32768"%\n%
  set /a "macro.Random.rtn=!macro.Random.rtn:-=!"%\n%
  set /a "macro.Random.rtn=!macro.Random.rtn! %% ((%%~b)-(%%~a)+1) + (%%~a)"%\n%
  for /f %%v in ("!macro.Random.rtn!") do (%\n%
    for /f %%w in ("!seed!") do (%\n%
       endlocal^&set %%~c=%%w^&if "%%~d" neq "" (set %%~d=%%v) else %echo%%%v%\n%
    )%\n%
  )%\n%
)

The results of this were absolutely shocking:

Code: Select all

Beginning test of 100 calls to Random(1-100) with seed -12345...
1 81 28 1 39 9 20 8 35 9 31 78 61 70 100 83 37 86 64 41 33 82 59 46 45 2 88 81 6
1 9 15 100 92 3 33 83 27 5 99 54 69 20 53 42 1 14 79 39 18 46 71 64 56 35 40 84
78 82 50 55 30 48 36 30 57 71 57 83 8 7 60 93 40 39 2 34 73 84 35 91 9 1 39 4 93
 69 59 75 11 78 33 76 43 44 52 41 30 5 15 65
time taken=00:00:10.17
Beginning test of 100 calls to macro.RandomSeed(1-100) with seed -12345...
1 81 28 1 39 9 20 8 35 9 31 78 61 70 100 83 37 86 64 41 33 82 59 46 45 2 88 81 6
1 9 15 100 92 3 33 83 27 5 99 54 69 20 53 42 1 14 79 39 18 46 71 64 56 35 40 84
78 82 50 55 30 48 36 30 57 71 57 83 8 7 60 93 40 39 2 34 73 84 35 91 9 1 39 4 93
 69 59 75 11 78 33 76 43 44 52 41 30 5 15 65
time taken=00:00:00.39

After this major breakthrough I rewrote my CharToNum and NumToChar functions as macros, as well as my LeftCol and RightCol functions (used to pad strings out to a given length or else chop them to size) and dropped each of those in macroLib_String.

Code: Select all

set macro\args.CharToNum=  CharVar  [RtnVar]
set macro\help.CharToNum=  CharVar  [RtnVar]%\n%
  %\n%
  Converts the character within variable CharVar to a number%\n%
  representing it's position in the alphabet (1-26)%\n%
  %\n%
  Sets RtnVar = result%\n%
  or displays result if RtnVar not specified%xLF%
%macro_BeginDef%
set macro.CharToNum=do (%\n%
  !macro_InitRtn!%\n%
  setlocal enableDelayedExpansion%\n%
  set "str=^!%%~a^!"%\n%
  for %%A in (%\n%
    "A=1" "B=2" "C=3" "D=4" "E=5" "F=6" "G=7" "H=8" "I=9"%\n%
    "J=10" "K=11" "L=12" "M=13" "N=14" "O=15" "P=16" "Q=17" "R=18"%\n%
    "S=19" "T=20" "U=21" "V=22" "W=23" "X=24" "Y=25" "Z=26"%\n%
  ) do set "str=^!str:%%~A^!"%\n%
  !macro_Call! ("^!errorlevel^! 1 str %%~b") !macro.Rtn1!%\n%
)
%macro_Call% ("macro.CharToNum") %macro.EndDef%
%macro_EndAnyRtn%

set macro\args.NumToChar=  CharVar  [RtnVar]
set macro\help.NumToChar=  CharVar  [RtnVar]%\n%
  %\n%
  Converts the number within variable CharVar to a%\n%
  character based on it's position in the alphabet%\n%
  %\n%
  Sets RtnVar = result%\n%
  or displays result if RtnVar not specified%xLF%
%macro_BeginDef%
set macro.NumToChar=do (%\n%
  !macro_InitRtn!%\n%
  setlocal enableDelayedExpansion%\n%
  set "str=^!%%~a^!"%\n%
  for %%A in (%\n%
    "10=J" "11=K" "12=L" "13=M" "14=N" "15=O" "16=P" "17=Q" "18=R"%\n%
    "19=S" "20=T" "21=U" "22=V" "23=W" "24=X" "25=Y" "26=Z"%\n%
    "1=A" "2=B" "3=C" "4=D" "5=E" "6=F" "7=G" "8=H" "9=I"%\n%
  ) do set "str=^!str:%%~A^!"%\n%
  !macro_Call! ("^!errorlevel^! 1 str %%~b") !macro.Rtn1!%\n%
)
%macro_Call% ("macro.NumToChar") %macro.EndDef%
%macro_EndAnyRtn%

set macro\args.LeftCol=  StrVar  SizeVar  [RtnVar]
set macro\help.LeftCol=  StrVar  SizeVar  [RtnVar]%\n%
  %\n%
  Converts the string within variable StrVar to a left aligned%\n%
  string of the desired size by padding with zeroes%\n%
  %\n%
  Sets RtnVar = result%\n%
  or displays result if RtnVar not specified%xLF%
%macro_BeginDef%
set macro.LeftCol=do (%\n%
  !macro_InitRtn!%\n%
  setlocal enableDelayedExpansion%\n%
  set "str=^!%%~a^!"%\n%
  set "size=^!%%~b^!"%\n%
  for /l %%A in (0,1,^^!size^^!) do (%\n%
    set "str=^!str^! "%\n%
  )%\n%
  for %%B in (^^!size^^!) do (%\n%
    set "str=^!str:~0,%%B^!"%\n%
  )%\n%
  !macro_Call! ("^!errorlevel^! 1 str %%~c") !macro.Rtn1!%\n%
)
%macro_Call% ("macro.LeftCol") %macro.EndDef%
%macro_EndAnyRtn%

set macro\args.RightCol=  StrVar  SizeVar  [RtnVar]
set macro\help.RightCol=  StrVar  SizeVar  [RtnVar]%\n%
  %\n%
  Converts the string within variable StrVar to a right aligned%\n%
  string of the desired size by padding with zeroes%\n%
  %\n%
  Sets RtnVar = result%\n%
  or displays result if RtnVar not specified%xLF%
%macro_BeginDef%
set macro.RightCol=do (%\n%
  !macro_InitRtn!%\n%
  setlocal enableDelayedExpansion%\n%
  set "str=^!%%~a^!"%\n%
  set "size=^!%%~b^!"%\n%
  for /l %%A in (0,1,^^!size^^!) do (%\n%
    set "str= ^!str^!"%\n%
  )%\n%
  for %%B in (^^!size^^!) do (%\n%
    set "str=^!str:~-%%B^!"%\n%
  )%\n%
  !macro_Call! ("^!errorlevel^! 1 str %%~c") !macro.Rtn1!%\n%
)
%macro_Call% ("macro.RightCol") %macro.EndDef%
%macro_EndAnyRtn%

With all of my main helper functions converted to macros, I changed each of my encryption ciphers to call these macros instead of their original functions. Below are the results:

Code: Select all

                                     76-character input           1000-character input
Purpose                           Function Time   Macro Time   Function Time   Macro Time
Monoalphabetic Encrypt           12.7s              5.1s            1m11s          52s
Monoalphabetic Decrypt           10.7s              1.3s            40s            2.2s
Polyalphabetic Encrypt           9.5s               3.4s            1m59s          43s
Polyalphabetic Decrypt           7.2s               1.5s            1m33s          19s
Vigenere Encrypt                 4.0s               4.5s            49s            1m20s
Vigenere Decrypt                 4.0s               4.6s            50s            1m24s
Running Vigenere Encrypt        4.0s               5.1s            49s             1m19s
Running Vigenere Decrypt        4.0s               4.7s            50s             1m32s
Enigma Encrypt/Decrypt          18.4s             5.2s            3m19s           1m38s

So somehow I made my vigenere cipher substantially slower (~50%) but all other ciphers greatly increased in speed. In general the ciphers look to be about 3x faster on short messages and 2x faster on large ones. There's something odd about the timing of the monoalphabetic decode... but the timings were repeatable? I'll look more into that, maybe I accidentally coded something well and I can take advantage of it in the other ciphers. I'm also not sure what happened to Vigenere, but I'll be taking a look at that next week to see if I can fix it. Otherwise I guess I can change it back to using functions instead of macros, but I can't imagine that that's the real problem...

In any case, it's becoming a distinct possibility that I'm only talking to myself here :) , so unless there's some other interest in encryption via batch I think I'll let this thread rest in peace.

jeb
Expert
Posts: 1055
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: String Encryption (encoding)

#18 Post by jeb » 06 Aug 2013 17:27

Magialisk wrote:In any case, it's becoming a distinct possibility that I'm only talking to myself here :) , so unless there's some other interest in encryption via batch I think I'll let this thread rest in peace.

No :) I'm interested :!:

I think a bit about an AES or XTEA implementation.
This should be possible with batch, perhaps not too fast, but possible.

Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: String Encryption (encoding)

#19 Post by Magialisk » 06 Aug 2013 21:41

jeb! How nice of you to stop by! Your work has been a huge inspiration, though I'm sure it doesn't show in any of my code snippets :oops:

I myself considered trying an AES implementation just as a challenge after I finished the Enigma simulator. The main part of the AES algorithm I don't understand well is the generator function in the key scheduler, but I suppose that could be learned. My bigger turn-off seemed to be the limitation of 32-bit unsigned variables in the batch environment...

The state matrices themselves didn't seem like they'd be that hard to manipulate, each is a 4x4 matrix of 8-bit bytes and round operations are performed byte by byte across the matrix, so I was thinking of modeling it as a 'S[x][y]' variable and writing macros/functions for SubBytes(), ShiftRows(), etc. The trickier part seemed to be breaking down a 128-bit key (or larger) and properly getting it through the key expansion algorithm to produce the proper round keys, each of which is four 32-bit words. I don't know the steps in the middle well enough to know if they would overflow 32-bit operations, etc. Inputs and outputs to the AES routine would also consist of four 32-bit "pieces" representing a total 128-bit value of course, so it just seemed like a lot of variable manipulation complexity compared to an environment that could handle 128-bit variables natively.

Some day when I'm not juggling a full-time job, a cross-county relocation, a two year old and a master's program I might just reconsider and try my hand at an AES batch. If that day ever comes I'm hoping you experts will be here to correct my mistakes and make it run 100,000 times faster :)

As a side note related to the original thread, I've improved my Poly-alphabetic and Vigenere ciphers to accept all typeable poison characters in the input/output strings. It was frustrating at first when I had them verified working through debug but couldn't get the resulting strings over the ENDLOCAL barrier, but then I remembered dbenham's AnyRtn macros and poof, the magic happened!

Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: String Encryption (encoding)

#20 Post by Magialisk » 08 Aug 2013 09:19

So I was bored yesterday which is always a dangerous thing. I studied up on the AES key expansion algorithm and it turned out to be just as simple as the rest of the algorithm. I probably should have guessed that would be the case.

Setting aside my embarassing inability to keep byte orders and word orders straight (which led to about a half-dozen complete re-writes of the below code) I worked up some code to perform the expansion for 128-bit keys inputs. I've tested this against the example key in the FIPS-197 standard as well as some random keys I punched into an online calculator. Here's the code:

Code: Select all

@echo off
::define a Carriage Return string, only useable as !CR!
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"

::define a Line Feed (newline) string (normally only used as !LF!)
set LF=^


::Above 2 blank lines are required - do not remove

::define a Line Feed string that can be used as %xLF%
set ^"xLF=^^^%LF%%LF%^%LF%%LF%"

::define a newline with line continuation
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

::define 'macro_call' and 'Num2Hex' macros
set macro_Call=for /f "tokens=1-26" %%a in
set macro.Num2Hex=do (%\n%
  setlocal enableDelayedExpansion%\n%
  set /a "dec=(%%~a)"%\n%
  if defined hex set "hex="%\n%
  set "map=0123456789ABCDEF"%\n%
  for /l %%n in (1,1,8) do (%\n%
    set /a "d=dec&15,dec>>=4"%\n%
    for %%d in (!d!) do set "hex=!map:~%%d,1!!hex!"%\n%
  )%\n%
  set hex=!hex:00=,!%\n%
  for /f "tokens=* delims=," %%v in ("!hex!") do set "hex=%%v"^&if not defined hex set "hex=00"%\n%
  for %%v in (!hex!) do endlocal^&if "%%~b" neq "" (set "%%~b=%%v") else %echo%%%v%\n%
)
SETLOCAL ENABLEDELAYEDEXPANSION

:: Pre-computed Rijndael S-box
set /a Sbox[0]=0x63 & set /a Sbox[1]=0x7c & set /a Sbox[2]=0x77 & set /a Sbox[3]=0x7b & set /a Sbox[4]=0xf2
set /a Sbox[5]=0x6b & set /a Sbox[6]=0x6f & set /a Sbox[7]=0xc5 & set /a Sbox[8]=0x30 & set /a Sbox[9]=0x01
set /a Sbox[10]=0x67 & set /a Sbox[11]=0x2b & set /a Sbox[12]=0xfe & set /a Sbox[13]=0xd7 & set /a Sbox[14]=0xab
set /a Sbox[15]=0x76 & set /a Sbox[16]=0xca & set /a Sbox[17]=0x82 & set /a Sbox[18]=0xc9 & set /a Sbox[19]=0x7d
set /a Sbox[20]=0xfa & set /a Sbox[21]=0x59 & set /a Sbox[22]=0x47 & set /a Sbox[23]=0xf0 & set /a Sbox[24]=0xad
set /a Sbox[25]=0xd4 & set /a Sbox[26]=0xa2 & set /a Sbox[27]=0xaf & set /a Sbox[28]=0x9c & set /a Sbox[29]=0xa4
set /a Sbox[30]=0x72 & set /a Sbox[31]=0xc0 & set /a Sbox[32]=0xb7 & set /a Sbox[33]=0xfd & set /a Sbox[34]=0x93
set /a Sbox[35]=0x26 & set /a Sbox[36]=0x36 & set /a Sbox[37]=0x3f & set /a Sbox[38]=0xf7 & set /a Sbox[39]=0xcc
set /a Sbox[40]=0x34 & set /a Sbox[41]=0xa5 & set /a Sbox[42]=0xe5 & set /a Sbox[43]=0xf1 & set /a Sbox[44]=0x71
set /a Sbox[45]=0xd8 & set /a Sbox[46]=0x31 & set /a Sbox[47]=0x15 & set /a Sbox[48]=0x04 & set /a Sbox[49]=0xc7
set /a Sbox[50]=0x23 & set /a Sbox[51]=0xc3 & set /a Sbox[52]=0x18 & set /a Sbox[53]=0x96 & set /a Sbox[54]=0x05
set /a Sbox[55]=0x9a & set /a Sbox[56]=0x07 & set /a Sbox[57]=0x12 & set /a Sbox[58]=0x80 & set /a Sbox[59]=0xe2
set /a Sbox[60]=0xeb & set /a Sbox[61]=0x27 & set /a Sbox[62]=0xb2 & set /a Sbox[63]=0x75 & set /a Sbox[64]=0x09
set /a Sbox[65]=0x83 & set /a Sbox[66]=0x2c & set /a Sbox[67]=0x1a & set /a Sbox[68]=0x1b & set /a Sbox[69]=0x6e
set /a Sbox[70]=0x5a & set /a Sbox[71]=0xa0 & set /a Sbox[72]=0x52 & set /a Sbox[73]=0x3b & set /a Sbox[74]=0xd6
set /a Sbox[75]=0xb3 & set /a Sbox[76]=0x29 & set /a Sbox[77]=0xe3 & set /a Sbox[78]=0x2f & set /a Sbox[79]=0x84
set /a Sbox[80]=0x53 & set /a Sbox[81]=0xd1 & set /a Sbox[82]=0x00 & set /a Sbox[83]=0xed & set /a Sbox[84]=0x20
set /a Sbox[85]=0xfc & set /a Sbox[86]=0xb1 & set /a Sbox[87]=0x5b & set /a Sbox[88]=0x6a & set /a Sbox[89]=0xcb
set /a Sbox[90]=0xbe & set /a Sbox[91]=0x39 & set /a Sbox[92]=0x4a & set /a Sbox[93]=0x4c & set /a Sbox[94]=0x58
set /a Sbox[95]=0xcf & set /a Sbox[96]=0xd0 & set /a Sbox[97]=0xef & set /a Sbox[98]=0xaa & set /a Sbox[99]=0xfb
set /a Sbox[100]=0x43 & set /a Sbox[101]=0x4d & set /a Sbox[102]=0x33 & set /a Sbox[103]=0x85 & set /a Sbox[104]=0x45
set /a Sbox[105]=0xf9 & set /a Sbox[106]=0x02 & set /a Sbox[107]=0x7f & set /a Sbox[108]=0x50 & set /a Sbox[109]=0x3c
set /a Sbox[110]=0x9f & set /a Sbox[111]=0xa8 & set /a Sbox[112]=0x51 & set /a Sbox[113]=0xa3 & set /a Sbox[114]=0x40
set /a Sbox[115]=0x8f & set /a Sbox[116]=0x92 & set /a Sbox[117]=0x9d & set /a Sbox[118]=0x38 & set /a Sbox[119]=0xf5
set /a Sbox[120]=0xbc & set /a Sbox[121]=0xb6 & set /a Sbox[122]=0xda & set /a Sbox[123]=0x21 & set /a Sbox[124]=0x10
set /a Sbox[125]=0xff & set /a Sbox[126]=0xf3 & set /a Sbox[127]=0xd2 & set /a Sbox[128]=0xcd & set /a Sbox[129]=0x0c
set /a Sbox[130]=0x13 & set /a Sbox[131]=0xec & set /a Sbox[132]=0x5f & set /a Sbox[133]=0x97 & set /a Sbox[134]=0x44
set /a Sbox[135]=0x17 & set /a Sbox[136]=0xc4 & set /a Sbox[137]=0xa7 & set /a Sbox[138]=0x7e & set /a Sbox[139]=0x3d
set /a Sbox[140]=0x64 & set /a Sbox[141]=0x5d & set /a Sbox[142]=0x19 & set /a Sbox[143]=0x73 & set /a Sbox[144]=0x60
set /a Sbox[145]=0x81 & set /a Sbox[146]=0x4f & set /a Sbox[147]=0xdc & set /a Sbox[148]=0x22 & set /a Sbox[149]=0x2a
set /a Sbox[150]=0x90 & set /a Sbox[151]=0x88 & set /a Sbox[152]=0x46 & set /a Sbox[153]=0xee & set /a Sbox[154]=0xb8
set /a Sbox[155]=0x14 & set /a Sbox[156]=0xde & set /a Sbox[157]=0x5e & set /a Sbox[158]=0x0b & set /a Sbox[159]=0xdb
set /a Sbox[160]=0xe0 & set /a Sbox[161]=0x32 & set /a Sbox[162]=0x3a & set /a Sbox[163]=0x0a & set /a Sbox[164]=0x49
set /a Sbox[165]=0x06 & set /a Sbox[166]=0x24 & set /a Sbox[167]=0x5c & set /a Sbox[168]=0xc2 & set /a Sbox[169]=0xd3
set /a Sbox[170]=0xac & set /a Sbox[171]=0x62 & set /a Sbox[172]=0x91 & set /a Sbox[173]=0x95 & set /a Sbox[174]=0xe4
set /a Sbox[175]=0x79 & set /a Sbox[176]=0xe7 & set /a Sbox[177]=0xc8 & set /a Sbox[178]=0x37 & set /a Sbox[179]=0x6d
set /a Sbox[180]=0x8d & set /a Sbox[181]=0xd5 & set /a Sbox[182]=0x4e & set /a Sbox[183]=0xa9 & set /a Sbox[184]=0x6c
set /a Sbox[185]=0x56 & set /a Sbox[186]=0xf4 & set /a Sbox[187]=0xea & set /a Sbox[188]=0x65 & set /a Sbox[189]=0x7a
set /a Sbox[190]=0xae & set /a Sbox[191]=0x08 & set /a Sbox[192]=0xba & set /a Sbox[193]=0x78 & set /a Sbox[194]=0x25
set /a Sbox[195]=0x2e & set /a Sbox[196]=0x1c & set /a Sbox[197]=0xa6 & set /a Sbox[198]=0xb4 & set /a Sbox[199]=0xc6
set /a Sbox[200]=0xe8 & set /a Sbox[201]=0xdd & set /a Sbox[202]=0x74 & set /a Sbox[203]=0x1f & set /a Sbox[204]=0x4b
set /a Sbox[205]=0xbd & set /a Sbox[206]=0x8b & set /a Sbox[207]=0x8a & set /a Sbox[208]=0x70 & set /a Sbox[209]=0x3e
set /a Sbox[210]=0xb5 & set /a Sbox[211]=0x66 & set /a Sbox[212]=0x48 & set /a Sbox[213]=0x03 & set /a Sbox[214]=0xf6
set /a Sbox[215]=0x0e & set /a Sbox[216]=0x61 & set /a Sbox[217]=0x35 & set /a Sbox[218]=0x57 & set /a Sbox[219]=0xb9
set /a Sbox[220]=0x86 & set /a Sbox[221]=0xc1 & set /a Sbox[222]=0x1d & set /a Sbox[223]=0x9e & set /a Sbox[224]=0xe1
set /a Sbox[225]=0xf8 & set /a Sbox[226]=0x98 & set /a Sbox[227]=0x11 & set /a Sbox[228]=0x69 & set /a Sbox[229]=0xd9
set /a Sbox[230]=0x8e & set /a Sbox[231]=0x94 & set /a Sbox[232]=0x9b & set /a Sbox[233]=0x1e & set /a Sbox[234]=0x87
set /a Sbox[235]=0xe9 & set /a Sbox[236]=0xce & set /a Sbox[237]=0x55 & set /a Sbox[238]=0x28 & set /a Sbox[239]=0xdf
set /a Sbox[240]=0x8c & set /a Sbox[241]=0xa1 & set /a Sbox[242]=0x89 & set /a Sbox[243]=0x0d & set /a Sbox[244]=0xbf
set /a Sbox[245]=0xe6 & set /a Sbox[246]=0x42 & set /a Sbox[247]=0x68 & set /a Sbox[248]=0x41 & set /a Sbox[249]=0x99
set /a Sbox[250]=0x2d & set /a Sbox[251]=0x0f & set /a Sbox[252]=0xb0 & set /a Sbox[253]=0x54 & set /a Sbox[254]=0xbb
set /a Sbox[255]=0x16

:: Pre-computed round constants for generator algorithm
set /a RC[1]=0x01 & set /a RC[2]=0x02 & set /a RC[3]=0x04 & set /a RC[4]=0x08 & set /a RC[5]=0x10
set /a RC[6]=0x20 & set /a RC[7]=0x40 & set /a RC[8]=0x80 & set /a RC[9]=0x1b & set /a RC[10]=0x36

:: Assume a 128-bit key obtained as an input parameter.  This test key is from the FIPS-197 standard.
set "key=2b7e151628aed2a6abf7158809cf4f3c"

:: Process key from left to right as bytes 0 through 15
:: Rearrange the key into 4 words of 4 bytes each and insert as columns in a state matrix
::   W0   W1   W2   W3        key[r][c] definitions
::  -------------------       ---------------------
::   B0 | B4 | B8 | B12       0,0 | 0,1 | 0,2 | 0,3
::   B1 | B5 | B9 | B13   =   1,0 | 1,1 | 1,2 | 1,3
::   B2 | B6 | B10| B14       2,0 | 2,1 | 2,2 | 2,3
::   B3 | B7 | B11| B15       3,0 | 3,1 | 3,2 | 3,3
set /a "key[0][0]=0x!key:~0,2!"
set /a "key[1][0]=0x!key:~2,2!"
set /a "key[2][0]=0x!key:~4,2!"
set /a "key[3][0]=0x!key:~6,2!"
set /a "key[0][1]=0x!key:~8,2!"
set /a "key[1][1]=0x!key:~10,2!"
set /a "key[2][1]=0x!key:~12,2!"
set /a "key[3][1]=0x!key:~14,2!"
set /a "key[0][2]=0x!key:~16,2!"
set /a "key[1][2]=0x!key:~18,2!"
set /a "key[2][2]=0x!key:~20,2!"
set /a "key[3][2]=0x!key:~22,2!"
set /a "key[0][3]=0x!key:~24,2!"
set /a "key[1][3]=0x!key:~26,2!"
set /a "key[2][3]=0x!key:~28,2!"
set /a "key[3][3]=0x!key:~30,2!"

:: Display input key (round 0 key) to verify proper translation into state matrix
set /p ans="round 0 key: " <nul
FOR /L %%a IN (0,1,3) DO FOR /L %%b IN (0,1,3) DO (
   %macro_call% ("!key[%%b][%%a]! hex") %macro.Num2Hex%
   set /p ans="!hex!" <nul
)
echo.

:: A 128-bit key dictates 10 rounds so we need to expand 10*4=40 additional words for round keys
FOR /L %%a IN (4,1,43) DO (
   set /a mod=%%a %% 4
   :: If this is the first word of a new round key, start the generator algorithm
   IF !mod!==0 (
      set /a lastword=%%a-1
      FOR %%b IN (!lastword!) DO (
         :: First step is a one byte circular left shift of the previous word's values
         set tempword[0]=!key[1][%%b]!
         set tempword[1]=!key[2][%%b]!
         set tempword[2]=!key[3][%%b]!
         set tempword[3]=!key[0][%%b]!
         :: Next we substitute all bytes per the pre-computed S-box
         FOR /L %%c IN (0,1,3) DO FOR %%d IN (!tempword[%%c]!) DO set /a tempword[%%c]=!Sbox[%%d]!
         :: Then we XOR the first byte with this round's pre-computed round constant
         set /a round=%%a/4
         FOR %%c IN (!round!) DO (
            set /a key[0][%%a]=!tempword[0]!^^^^!RC[%%c]!
            set key[1][%%a]=!tempword[1]!
            set key[2][%%a]=!tempword[2]!
            set key[3][%%a]=!tempword[3]!
         )
      )
      :: Generator algorithm complete.  Now we XOR the result with the first word of the last round
      set /a last=%%a-4
      FOR %%b IN (!last!) DO (
         set /a key[0][%%a]=!key[0][%%b]!^^^^!key[0][%%a]!
         set /a key[1][%%a]=!key[1][%%b]!^^^^!key[1][%%a]!
         set /a key[2][%%a]=!key[2][%%b]!^^^^!key[2][%%a]!
         set /a key[3][%%a]=!key[3][%%b]!^^^^!key[3][%%a]!
      )
   )
   :: The 2nd through 4th words in each round are simply formed by XOR operations between%\n%
      the immediately previous word and the word in the same position from the previous round
   IF NOT !mod!==0 (
      set /a first=%%a-1
      set /a second=%%a-4
      FOR %%b IN (!first!) DO FOR %%c IN (!second!) DO (
         set /a key[0][%%a]=!key[0][%%b]!^^^^!key[0][%%c]!
         set /a key[1][%%a]=!key[1][%%b]!^^^^!key[1][%%c]!
         set /a key[2][%%a]=!key[2][%%b]!^^^^!key[2][%%c]!
         set /a key[3][%%a]=!key[3][%%b]!^^^^!key[3][%%c]!
      )
   )
)
:: Display the full set of expanded keys for rounds 1 through 10
FOR /L %%a IN (4,4,40) DO (
   set /a round=%%a/4
   set /p ans="round !round! key: " <nul
   set /a n=%%a+1 & set /a nn=%%a+2 & set /a nnn=%%a+3
   FOR /L %%b IN (0,1,3) DO (
      %macro_call% ("!key[%%b][%%a]! hex") %macro.Num2Hex%
      set /p ans="!hex!" <nul
   )
   FOR %%b IN (!n!) DO FOR /L %%c IN (0,1,3) DO (
      %macro_call% ("!key[%%c][%%b]! hex") %macro.Num2Hex%
      set /p ans="!hex!" <nul
   )
   FOR %%b IN (!nn!) DO FOR /L %%c IN (0,1,3) DO (
      %macro_call% ("!key[%%c][%%b]! hex") %macro.Num2Hex%
      set /p ans="!hex!" <nul
   )
   FOR %%b IN (!nnn!) DO FOR /L %%c IN (0,1,3) DO (
      %macro_call% ("!key[%%c][%%b]! hex") %macro.Num2Hex%
      set /p ans="!hex!" <nul
   )
   echo.
)
GOTO:EOF

And the results:

Code: Select all

C:\BTP\AES>aes
round 0 key: 2B7E151628AED2A6ABF7158809CF4F3C
round 1 key: A0FAFE1788542CB123A339392A6C7605
round 2 key: F2C295F27A96B9435935807A7359F67F
round 3 key: 3D80477D4716FE3E1E237E446D7A883B
round 4 key: EF44A541A8525B7FB671253BDB0BAD00
round 5 key: D4D1C6F87C839D87CAF2B8BC11F915BC
round 6 key: 6D88A37A110B3EFDDBF98641CA0093FD
round 7 key: 4E54F70E5F5FC9F384A64FB24EA6DC4F
round 8 key: EAD27321B58DBAD2312BF5607F8D292F
round 9 key: AC7766F319FADC2128D12941575C006E
round 10 key: D014F9A8C9EE2589E13F0CC8B6630CA6
C:\BTP\AES>

The code looks longer than it really is, given all the setup and declarations at the top, and the loop to display the output at the bottom. The core logic is surprisingly short and simple. It could stand to be optimized, but I was so excited it was finally working I wanted to post it.

As an added bonus, since the Sbox is already defined, that takes care of one of the additional four operations required to actually perform an encryption, ie: SubBytes(). ShiftRows() and AddRoundKey() are extremely simple to code, so I just have to figure out MixColumns() and we'll be in the encoding business. I'm not looking forward to typing out the inverse Sbox for decryption when the time comes, but this is actually somewhat entertaining so far. I might have to find some time to be bored again next week... :D

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: String Encryption (encoding)

#21 Post by Squashman » 08 Aug 2013 10:15

Magialisk wrote:The code looks longer than it really is, given all the setup and declarations at the top, and the loop to display the output at the bottom.


I would think you could setup nested for loops to define all the variables instead of hard coding it.

Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: String Encryption (encoding)

#22 Post by Magialisk » 08 Aug 2013 11:21

Absolutely I could, and I considered doing that so that I wouldn't have to type out 255 Sbox entries (and later the 255 inverse Sbox entries as well). However, I also looked at this as a time-memory tradeoff problem.

By pre-computing and hard-coding the values the code takes up more memory space, but it will execute in less time since each transformation is only a table lookup. With nested for loops and the complicated algorithmic logic required to compute the Sbox contents, the script would take a lot more time to run but theoretically save on memory space.

That, and if I'm being completely honest I don't really understand the concept of calculating the multiplicative inverse of a value over Rijndeael's finite polynomial field and then performing an affine transformation on that value :shock: . I'm a computer engineer, not a math major, so it seemed a lot easier to type out the pre-computed values than to write the code to calculate them :P. If someone with a better understanding of the mathematics wants to write an alogirthm to calculate the Sbox and inverse Sbox I'd love to play around with it.

Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: String Encryption (encoding)

#23 Post by Magialisk » 09 Aug 2013 08:09

Phew, it turned out the MixColumns() function was quite a bit more of a pain than I gave it credit for. What I initially categorized as "a simple matrix multiplication with special rules about carrying" turned out to be completely off base. Instead the operation relies on multiplication over the Rijndael Galois field, so I was quickly back to being the non-math major who doesn't know what a "Galois field" even means.

I strongly considered hard-coding pre-computed constants for all the possible multiplications, but it turns out that would ahve required six lookup tables with 255 values each to cover multiplying by 2, 3, 9, 11, 13 and 14. Even I draw the line at hand typing over 1500 "set /a thing[n]=0xValue" entries, so I had no choice but to fnd an algorithm and code it.

It turns out the code ended up pretty small:

Code: Select all

::define 'GaloisMult' macro
::takes two multiplicands as inputs plus a variable name in which to store the product over the Rijndael Galois field
set macro.GaloisMult=do (%\n%
  setlocal enableDelayedExpansion%\n%
  set /a "multA=(%%~a)"%\n%
  set /a "multB=(%%~b)"%\n%
  set "product=0"%\n%
  set "carry=0"%\n%
  for /l %%n in (1,1,8) do (%\n%
    set /a "lsbtest=!multB!^&0x01"%\n%
    if !lsbtest!==1 set /a "product=!product!^^!multA!"%\n%
    if !product! GTR 255 set /a "product=!product!-256"%\n%
    set /a "carry=!multA!^&0x80"%\n%
    set /a "multA=!multA!<<1"%\n%
    if !multA! GTR 255 set /a "multA=!multA!-256"%\n%
    if not !carry!==0 set /a "multA=!multA!^^0x1b"%\n%
    set /a "multB=!multB!>>1"%\n%
  )%\n%
  for %%v in (!product!) do endlocal^&set "%%~c=%%v"%\n%
)

That macro does what it says, and I spot-checked it against a reasonable number of values from online pre-computed tables, so it should be good to go. It could be optimized by breaking out of the FOR loop early if either multiplicand reaches 0, but I couldn't figure out how to break a FOR loop that is part of a macro definition? Does anyone have any tips there?

I tried adding "if !multA!==0 for %%v in (!product!) do endlocal^&set "%%~c=%%v"%\n%" at the top, along with the same check against MultB, and it "seems" to work (the correct product comes back) but I get a bunch of "missing operand" errors as the loop continues to expand in the background instead of actually breaking. I tried to append "^& exit" to the above line, which closes my whole CMD window, and I tried making a label just above the product return at the bottom and doing a GOTO at the top of the loop when A or B are 0, but that doesn't break at all and instead I get "setlocal has reached it maximum level of recursion" (whoops! :D)

Optimizing the macro by breaking early isn't exactly life or death, I just figure if we have to run all 16 elements of the state through 8 loop iterations per round, over 10 rounds, thats ~1300 loop iterations. And that's per every 128-bits of data we want to encrypt, so the number of loops could add up quick... It would be nice if the bytes that only needed 1-2 loops before hitting 0 would break early instead of wasting time doing 6-7 more empty loops. If anyone has any ideas I'd love to hear them. Thanks!

Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: String Encryption (encoding)

#24 Post by Magialisk » 09 Aug 2013 13:49

Well I'm officially halfway there. The encryption operation is now working, tested against the example values in the FIPS-197 standard. As expected it's pretty darn slow, though I haven't done anything intentional to optimize it, and I'm still looking for an answer to breaking a FOR loop inside a macro to speed up the Galois multiplication...

Using the standard test values it encrypted 128-bits in 1.81 seconds.

Code: Select all

C:\BTP\AES>aes
Output data: 3925841D02DC09FBDC118597196A0B32
Elapsed Time:  00:00:01.81

I can optionally have it output a bunch of debugging data as well that shows the values at every step along the way. Here's an example of that data:

Code: Select all

C:\BTP\AES>set aes_debug=1 & aes
input key: 2B7E151628AED2A6ABF7158809CF4F3C
input data: 3243F6A8885A308D313198A2E0370734

Expanded round keys:
round 1 key: A0FAFE1788542CB123A339392A6C7605
round 2 key: F2C295F27A96B9435935807A7359F67F
round 3 key: 3D80477D4716FE3E1E237E446D7A883B
round 4 key: EF44A541A8525B7FB671253BDB0BAD00
round 5 key: D4D1C6F87C839D87CAF2B8BC11F915BC
round 6 key: 6D88A37A110B3EFDDBF98641CA0093FD
round 7 key: 4E54F70E5F5FC9F384A64FB24EA6DC4F
round 8 key: EAD27321B58DBAD2312BF5607F8D292F
round 9 key: AC7766F319FADC2128D12941575C006E
round 10 key: D014F9A8C9EE2589E13F0CC8B6630CA6

Initial AddRoundKey result: 193DE3BEA0F4E22B9AC68D2AE9F84808

Begin round #1:
SubBytes result:    D42711AEE0BF98F1B8B45DE51E415230
ShiftRows result:   D4BF5D30E0B452AEB84111F11E2798E5
Galois*2 result:    B365BA60DB73A4476B8222F93C4E2BD1
Galois*3 result:    67DAE7503BC7F6E9D3C333082269B334
MixColumns result:  046681E5E0CB199A48F8D37A2806264C
Round key:          A0FAFE1788542CB123A339392A6C7605
AddRoundKey result: A49C7FF2689F352B6B5BEA43026A5049

Begin round #2:
SubBytes result:    49DED28945DB96F17F39871A7702533B
ShiftRows result:   49DB873B453953897F02D2F177DE961A
Galois*2 result:    92AD15768A72A609FE04BFF9EEA73734
Galois*3 result:    DB76924DCF4BF58081066D089979A12E
MixColumns result:  584DCAF11B4B5AACDBE7CAA81B6BB0E5
Round key:          F2C295F27A96B9435935807A7359F67F
AddRoundKey result: AA8F5F0361DDE3EF82D24AD26832469A
.
.
.
Begin round #9:
SubBytes result:    87EC4A8CF26EC3D84D4C46959790E7A6
ShiftRows result:   876E46A6F24CE78C4D904AD897ECC395
Galois*2 result:    15DC8C57FF98D5039A3B94AB35C39D31
Galois*3 result:    92B2CAF10DD4328FD7ABDE73A22F5EA4
MixColumns result:  473794ED40D4E4A5A3703AA64C9F42BC
Round key:          AC7766F319FADC2128D12941575C006E
AddRoundKey result: EB40F21E592E38848BA113E71BC342D2

Begin round #10:
SubBytes result:    E9098972CB31075F3D327D94AF2E2CB5
ShiftRows result:   E9317DB5CB322C723D2E895FAF090794
Galois*2 result:    15DC8C57FF98D5039A3B94AB35C39D31<-- same as round 9
Galois*3 result:    92B2CAF10DD4328FD7ABDE73A22F5EA4<-- same as round 9
MixColumns result:  E9317DB5CB322C723D2E895FAF090794<-- same as ShiftRows
Round key:          D014F9A8C9EE2589E13F0CC8B6630CA6
AddRoundKey result: 3925841D02DC09FBDC118597196A0B32

Output data: 3925841D02DC09FBDC118597196A0B32
Elapsed Time:  00:00:06.37

Decryption requires coding the inverses of each of the four round operations, so there's still plenty to be done, but if you can write code forward you can write it backwards, right? :D

Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: String Encryption (encoding)

#25 Post by Magialisk » 10 Aug 2013 14:18

No progress as of yet on the decryption side of the algorithm, though I did tweak the encryption to be considerably faster.

I have no idea why but it seems my scripts are often significantly slower on my Win7 PC than my XP PC. This was no exception, as the exact same operation that I posted yesterday that took 1.81s on a WinXP machine took 6.92s on Win7. In debug mode (which took 19.72s in Win7!) there was a noticeable pause of at least a second every time the script hit the Galois multiplication calculations.

I decided that the only hope for this script ever being usable was to get rid of the real-time Galois calculations and hard-code the pre-computed values as lookup tables, just like I'd done for the Sbox before. Fortunately, instead of having to type out 1500+ table entries by hand, I was able to write a short script to repeatedly call my GaloisMult macro and echo the results to a file in a nicely formatted lookup table.

In case anyone's interested, here's the script that I used to generate the six required tables:

Code: Select all

FOR %%n IN (2 3 9 11 13 14) DO (
   FOR /L %%a IN (0,5,250) DO (
      set /a p1=%%a+1
      set /a p2=%%a+2
      set /a p3=%%a+3
      set /a p4=%%a+4
      %macro_call% ("%%n %%a out") %macro.GaloisMult%
      %macro_call% ("!out! hex") %macro.Num2Hex%
      set Galois[%%a]=!hex!
      %macro_call% ("%%n !p1! out") %macro.GaloisMult%
      %macro_call% ("!out! hex") %macro.Num2Hex%
      set Galois[!p1!]=!hex!
      %macro_call% ("%%n !p2! out") %macro.GaloisMult%
      %macro_call% ("!out! hex") %macro.Num2Hex%
      set Galois[!p2!]=!hex!
      %macro_call% ("%%n !p3! out") %macro.GaloisMult%
      %macro_call% ("!out! hex") %macro.Num2Hex%
      set Galois[!p3!]=!hex!
      %macro_call% ("%%n !p4! out") %macro.GaloisMult%
      %macro_call% ("!out! hex") %macro.Num2Hex%
      set Galois[!p4!]=!hex!
      FOR %%b IN (!p1!) DO FOR %%c IN (!p2!) DO FOR %%d IN (!p3!) DO FOR %%e IN (!p4!) DO (
         echo set /a G%%n[%%a]=0x!Galois[%%a]! ^& set /a G%%n[%%b]=0x!Galois[%%b]! ^& set /a G%%n[%%c]=0x!Galois[%%c]! ^& set /a G%%n[%%d]=0x!Galois[%%d]! ^& set /a G%%n[%%e]=0x!Galois[%%e]!>> GaloisTables.txt
      )
   )
   %macro_call% ("%%n 255 out") %macro.GaloisMult%
   %macro_call% ("!out! hex") %macro.Num2Hex%
   echo set /a G%%n[255]=0x!hex!>> GaloisTables.txt
)
GOTO:EOF

The tables take up a LOT of space (~33.5 KB - going back to that time/memory tradeoff discussion), but at least they're clean and tidy and I didn't have to type them :wink:. Here's a sample snippet from where one ends and the next begins:

Code: Select all

set /a G2[240]=0xFB & set /a G2[241]=0xF9 & set /a G2[242]=0xFF & set /a G2[243]=0xFD & set /a G2[244]=0xF3
set /a G2[245]=0xF1 & set /a G2[246]=0xF7 & set /a G2[247]=0xF5 & set /a G2[248]=0xEB & set /a G2[249]=0xE9
set /a G2[250]=0xEF & set /a G2[251]=0xED & set /a G2[252]=0xE3 & set /a G2[253]=0xE1 & set /a G2[254]=0xE7
set /a G2[255]=0xE5
set /a G3[0]=0x00 & set /a G3[1]=0x03 & set /a G3[2]=0x06 & set /a G3[3]=0x05 & set /a G3[4]=0x0C
set /a G3[5]=0x0F & set /a G3[6]=0x0A & set /a G3[7]=0x09 & set /a G3[8]=0x18 & set /a G3[9]=0x1B
set /a G3[10]=0x1E & set /a G3[11]=0x1D & set /a G3[12]=0x14 & set /a G3[13]=0x17 & set /a G3[14]=0x12

With these tables copied into the main AES script, I rewrote the MixColumns function to simply perform a lookup/substitution instead of calculating the Galois product on the fly. Re-running the encryption test on my Win7 PC now takes only 0.74s, a reduction of nearly 90% :!:. I have a couple other less impressive optimizations in mind that I want to try out, and then at some point I'll have to write the decryption side of the cipher...

jeb
Expert
Posts: 1055
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: String Encryption (encoding)

#26 Post by jeb » 11 Aug 2013 03:37

Hi Magialisk,

very nice to see that a working AES algorithm is possible with batch files. :D

The Galois lookup tables are a good solution, and I suppose that it's the normal way to use them.
I only avoid lookup tables in some of my small embedded projects with little memory.

The initialisation of the sboxes could be a bit smaller.

Code: Select all

set ^"sbox=637C777BF26B6FC53001672BFED7AB76CA82C97DFA5947F0ADD4A2AF9CA472C0B7FD9326363FF7CC34A5E5F171D8311504C723C31896059A071280E2EB27B275^
09832C1A1B6E5AA0523BD6B329E32F8453D100ED20FCB15B6ACBBE394A4C58CFD0EFAAFB434D338545F9027F503C9FA851A3408F929D38F5BCB6DA2110FFF3D2^
CD0C13EC5F974417C4A77E3D645D197360814FDC222A908846EEB814DE5E0BDBE0323A0A4906245CC2D3AC629195E479E7C8376D8DD54EA96C56F4EA657AAE08^
BA78252E1CA6B4C6E8DD741F4BBD8B8A703EB5664803F60E613557B986C11D9EE1F8981169D98E949B1E87E9CE5528DF8CA1890DBFE6426841992D0FB054BB16"
setlocal EnableDelayedExpansion
for /L %%n in (0 1 255) Do (
   set /a pos=%%n*2
   for %%p in (!pos!) do set "val=!sbox:~%%p,2!"
   set /a Sbox[%%n]=0x!val!
)


jeb

Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: String Encryption (encoding)

#27 Post by Magialisk » 11 Aug 2013 06:28

Thanks jeb for that tip! That's similar to how I built the Galois tables but I didn't think to store only long strings and transform them into tables at the start of execution.

In fact at first looking at your code I was thinking "nah, I don't want to run a bunch of extra for loops at the start of the program to generate all my tables". I was envisioning someone running AES in a loop, creating a new setlocal each time, and every time it starts it has to re-expand the tables, etc.

Then I had a potential revelation. What if I just said screw the tables and kept the values in the single long strings. Why bother expanding them at all? Here's what my current one-liner looks like for the SubBytes operation, using the Sbox lookup table (previously renamed S[]):

Code: Select all

FOR /L %%b IN (0,1,3) DO FOR /L %%c IN (0,1,3) DO FOR %%d IN (!state[%%b][%%c]!) DO set /a state[%%b][%%c]=!S[%%d]!

Using your suggestion, I could change the one-liner to the below and the only difference is two extra set statements per loop at runtime:

Code: Select all

FOR /L %%b IN (0,1,3) DO FOR /L %%c IN (0,1,3) DO FOR %%d IN (!state[%%b][%%c]!) DO (
   set /a pos=2*%%d
   FOR %%e IN (!pos!) DO set "val=!sbox:~%%e,2!"
   set /a "state[%%b][%%c]=0x!val!"
)

I gave this a whirl and it works great. I really do like the concept, but of course it slows down execution slightly due to the long string parse. I ran the script three times each way and got these results:

Code: Select all

Original one-liner FOR:  0.76s, 0.72s, 0.79s
Modified parsing FOR:   0.94s, 0.89s, 0.94s

The cost of the shortened Sbox definition was anywhere from 100ms to 220ms, and if I wanted to do this for the Galois tables as well they'd surely add another ~150ms each. It's too bad because I love the concept, but I can't spare the execution time. As is I'm only running about 21 Bytes/sec on my Win7 machine (nearly 3 ascii characters per second), so a 1KB file should take about a minute to encrypt, allowing some time for ascii->hex pre-conversion.

I'll be the first to admit this is realistically only an educational/demonstration/"look what batch can do" exercise vs. anything ultimately practical, but for now I want to aim for speed wherever it's easy to find... Thanks again for both your tip and your encouragement. I'll report back with full source once I get decryption coded.

Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: String Encryption (encoding)

#28 Post by Magialisk » 11 Aug 2013 13:46

I got peeved off at a "Format String Vulnerability" lab I'm supposed to be working on so I blew off some steam by coding the decryption side of my AES cipher this afternoon:

Code: Select all

C:\Users\Marc\Desktop>aes enc
Test Data=00112233445566778899aabbccddeeff
Test Key=000102030405060708090a0b0c0d0e0f
Output data: 69C4E0D86A7B0430D8CDB78070B4C55A
Elapsed Time:  00:00:00.69

C:\Users\Marc\Desktop>aes dec
Test Data=69c4e0d86a7b0430d8cdb78070b4c55a
Test Key=000102030405060708090a0b0c0d0e0f
Output data: 00112233445566778899AABBCCDDEEFF
Elapsed Time:  00:00:00.70

So as is right now it's good to go for 128-bit keys and 128-bit (32-char) hex blocks. You could conceivably run it in a loop and pass it 32 characters per iteration and have the equivalent of Electronic Codebook (ECB) mode. I still have a lot of other features in mind that sound interesting, tweaking the key schedule and number of rounds to support 192- and 256-bit keys, file encryption (and more importantly, reassembly from decrypted hex to ascii), CBC/CTR modes, etc.

If anyone is interested in seeing the code let me know. It's still a bit sloppy as I'm adding things frequently, but the core of the cipher code is short and sweet so I could extract that out easily enough.

Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: String Encryption (encoding)

#29 Post by Magialisk » 11 Aug 2013 21:22

Let it be said that the 256-bit version of the key scheduling algorithm was a pain in my rear. Nothing like MixColumns(), but not friendly either. Not only that, but all the additional logic of checking "is this a 256-bit key", "is this word number a half-multiple vs. a full-multiple" etc. slowed my execution down by about 200ms. I'm going to have to dig in a bit and see if anything can be optimized, but at least 192- and 256-bit keys are now supported on the encode side. I'll have to go back and tweak the decode side to support them, as it's currently still hard-coded for 10 cipher rounds. It should only take a few minutes based on the minor changes required on the encode side, but it's quite late so I'm calling it a night.

Code: Select all

C:\Users\Marc\Desktop>aes enc
Test Data=00112233445566778899aabbccddeeff
Test Key=000102030405060708090a0b0c0d0e0f
Output data: 69C4E0D86A7B0430D8CDB78070B4C55A
Elapsed Time:  00:00:00.92

C:\Users\Marc\Desktop>aes enc
Test Data=00112233445566778899aabbccddeeff
Test Key=000102030405060708090a0b0c0d0e0f1011121314151617
Output data: DDA97CA4864CDFE06EAF70A0EC0D7191
Elapsed Time:  00:00:01.08

C:\Users\Marc\Desktop>aes enc
Test Data=00112233445566778899aabbccddeeff
Test Key=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
Output data: 8EA2B7CA516745BFEAFC49904B496089
Elapsed Time:  00:00:01.19

Post Reply