String Encryption (encoding)
Moderator: DosItHelp
String Encryption (encoding)
Hello Everyone...
I am writing a batch file that I would later convert to a exe, but I would like to have a serial key system...
So I thought I could use the "for /f "tokens=3" %%p in ('reg query "HKLM\Software\Microsoft\Windows NT\CurrentVersion" /v ProductID') do set "productID=%%p">nul
" command to get windows ProductID and then my batch file could change every, char letter "1" to a string "123" for example...
So if the id was... (example) "123-456-789"
For every char "1" it would change it to "123" or what ever
so it would do every relivant char in the ID string...
Then I would have a whole jumbled up numbers...
123721548424567465465465465465465 for example . I could also use this method to encrypt a password string if a user decides to setup a password for the program, But is would save is in a password.bat on the system and in it would be "set "pass=%pass%"password.bat
Another thing. I could also do a safer method of maybi Writing and Reading and Deleting strings to the reg
Is there a guide to that?
I am writing a batch file that I would later convert to a exe, but I would like to have a serial key system...
So I thought I could use the "for /f "tokens=3" %%p in ('reg query "HKLM\Software\Microsoft\Windows NT\CurrentVersion" /v ProductID') do set "productID=%%p">nul
" command to get windows ProductID and then my batch file could change every, char letter "1" to a string "123" for example...
So if the id was... (example) "123-456-789"
For every char "1" it would change it to "123" or what ever
so it would do every relivant char in the ID string...
Then I would have a whole jumbled up numbers...
123721548424567465465465465465465 for example . I could also use this method to encrypt a password string if a user decides to setup a password for the program, But is would save is in a password.bat on the system and in it would be "set "pass=%pass%"password.bat
Another thing. I could also do a safer method of maybi Writing and Reading and Deleting strings to the reg
Is there a guide to that?
Re: String Encryption (encoding)
To encrypt a string you need a batch file like this.
To decrypt a string you need a batch file like this.
Code: Select all
@echo off
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
(set CHAR[0]=000) & (set CHAR[1]=111) & (set CHAR[2]=222) & (set CHAR[3]=333)
(set CHAR[4]=444) & (set CHAR[5]=555) & (set CHAR[6]=666) & (set CHAR[7]=777)
(set CHAR[8]=888) & (set CHAR[9]=999) & (set CHAR[-]=---)
set TestPass=123-456-789
set TestPass2=%TestPass%
set "NewPass="
:again
set char=%TestPass2:~0,1%
set TestPass2=%TestPass2:~1%
set NewPass=%NewPass%!CHAR[%char%]!
if not "%TestPass2%"=="" goto :again
echo old password: %TestPass%
echo new password: %NewPass%
To decrypt a string you need a batch file like this.
Code: Select all
@echo off
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
(set CHAR[000]=0) & (set CHAR[111]=1) & (set CHAR[222]=2) & (set CHAR[333]=3)
(set CHAR[444]=4) & (set CHAR[555]=5) & (set CHAR[666]=6) & (set CHAR[777]=7)
(set CHAR[888]=8) & (set CHAR[999]=9) & (set CHAR[---]=-)
set TestPass=111222333---444555666---777888999
set TestPass2=%TestPass%
set "NewPass="
:again
set char=%TestPass2:~0,3%
set TestPass2=%TestPass2:~3%
set NewPass=%NewPass%!CHAR[%char%]!
if not "%TestPass2%"=="" goto :again
echo old password: %TestPass%
echo new password: %NewPass%
Re: String Encryption (encoding)
OMG thanks so much :):) I realy needed this for a long long time I'll give it a try
Ya it works
Could you be a lamb and explain in detail how these "~0,1%" commands work
Thanks so much again
Ya it works
Could you be a lamb and explain in detail how these "~0,1%" commands work
Thanks so much again
Re: String Encryption (encoding)
Hello I have tried to change the encryption characters to my own...
::Encrypt
(set CHAR[A]=UDFMHD45) & (set CHAR[B]=H121FDGF) & (set CHAR[C]=FDGHJ56D) & (set CHAR[D]=FGSG54D6) & (set CHAR[E]=JUK456JH)
(set CHAR[F]=ERE4G54S) & (set CHAR[G]=T5H64FDD) & (set CHAR[H]=RG56F41G) & (set CHAR[I]=RG45FG4D) & (set CHAR[J]=RT8564F6)
(set CHAR[K]=VCBV5C3B) & (set CHAR[L]=FD8G9G2F) & (set CHAR[M]=FDG4CVJS) & (set CHAR[N]=FG4213FG) & (set CHAR[O]=FD456GC2)
(set CHAR[P]=TH56GDF5) & (set CHAR[Q]=CV54F6GR) & (set CHAR[R]=XDF64FTS) & (set CHAR[S]=X78DG9RT) & (set CHAR[T]=TGH74SDJ)
(set CHAR[U]=BCX856DF) & (set CHAR[V]=FGH654SD) & (set CHAR[W]=45KLD45D) & (set CHAR[X]=GF2H3FG2) & (set CHAR[Y]=GFH564GF)
(set CHAR[Z]=45TG21FG) & (set CHAR[1]=D45G213D) & (set CHAR[2]=GB456DFG) & (set CHAR[3]=SDF456GF) & (set CHAR[4]=PF6F1G32)
(set CHAR[5]=FD6DFGG1) & (set CHAR[6]=56DFG54G) & (set CHAR[7]=UISG4FDG) & (set CHAR[8]=FKJH6FDG) & (set CHAR[9]=IFDGJHK6)
::Decrypt
(set CHAR[UDFMHD45]=A) & (set CHAR[H121FDGF]=B) & (set CHAR[FDGHJ56D]=C) & (set CHAR[FGSG54D6]=D) & (set CHAR[JUK456JH]=E)
(set CHAR[ERE4G54S]=F) & (set CHAR[T5H64FDD]=G) & (set CHAR[RG56F41G]=H) & (set CHAR[RG45FG4D]=I) & (set CHAR[RT8564F6]=J)
(set CHAR[VCBV5C3B]=K) & (set CHAR[FD8G9G2F]=L) & (set CHAR[FDG4CVJS]=M) & (set CHAR[FG4213FG]=N) & (set CHAR[FD456GC2]=O)
(set CHAR[TH56GDF5]=P) & (set CHAR[CV54F6GR]=Q) & (set CHAR[XDF64FTS]=R) & (set CHAR[X78DG9RT]=S) & (set CHAR[TGH74SDJ]=T)
(set CHAR[BCX856DF]=U) & (set CHAR[FGH654SD]=V) & (set CHAR[45KLD45D]=W) & (set CHAR[GF2H3FG2]=X) & (set CHAR[GFH564GF]=Y)
(set CHAR[45TG21FG]=Z) & (set CHAR[D45G213D]=1) & (set CHAR[GB456DFG]=2) & (set CHAR[SDF456GF]=3) & (set CHAR[PF6F1G32]=4)
(set CHAR[FD6DFGG1]=5) & (set CHAR[56DFG54G]=6) & (set CHAR[UISG4FDG]=7) & (set CHAR[FKJH6FDG]=8) & (set CHAR[IFDGJHK6]=9)
But it screws it's selft up. Do i need to change them to numbers only? example
set CHAR[85174187]=A
set CHAR[A]=85174187
My "changed" version does want to work ...
@echo off
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
title Decrypt
:mainmenu
(set CHAR[UDFMHD45]=A) & (set CHAR[H121FDGF]=B) & (set CHAR[FDGHJ56D]=C) & (set CHAR[FGSG54D6]=D) & (set CHAR[JUK456JH]=E)
(set CHAR[ERE4G54S]=F) & (set CHAR[T5H64FDD]=G) & (set CHAR[RG56F41G]=H) & (set CHAR[RG45FG4D]=I) & (set CHAR[RT8564F6]=J)
(set CHAR[VCBV5C3B]=K) & (set CHAR[FD8G9G2F]=L) & (set CHAR[FDG4CVJS]=M) & (set CHAR[FG4213FG]=N) & (set CHAR[FD456GC2]=O)
(set CHAR[TH56GDF5]=P) & (set CHAR[CV54F6GR]=Q) & (set CHAR[XDF64FTS]=R) & (set CHAR[X78DG9RT]=S) & (set CHAR[TGH74SDJ]=T)
(set CHAR[BCX856DF]=U) & (set CHAR[FGH654SD]=V) & (set CHAR[45KLD45D]=W) & (set CHAR[GF2H3FG2]=X) & (set CHAR[GFH564GF]=Y)
(set CHAR[45TG21FG]=Z) & (set CHAR[D45G213D]=1) & (set CHAR[GB456DFG]=2) & (set CHAR[SDF456GF]=3) & (set CHAR[PF6F1G32]=4)
(set CHAR[FD6DFGG1]=5) & (set CHAR[56DFG54G]=6) & (set CHAR[UISG4FDG]=7) & (set CHAR[FKJH6FDG]=8) & (set CHAR[IFDGJHK6]=9)
(set CHAR[HG45J6FG]=.) & (set CHAR[DF456HG4]=,)
echo Enter a string to decrypt:
set /p Decrypt=
set Decrypt2=%Decrypt%
set "DecryptOut="
:decrypt2
set char=%Decrypt2:~0,3%
set Decrypt2=%Decrypt2:~3%
set DecryptOut=%DecryptOut%!CHAR[%char%]!
if not "%Decrypt2%"=="" goto decrypt2
echo Input string: %Decrypt%
echo Output string: %DecryptOut%
echo.
pause
cls
goto mainmenu
Thanks for your time
P.S this forum is really great I love it with all your help
::Encrypt
(set CHAR[A]=UDFMHD45) & (set CHAR[B]=H121FDGF) & (set CHAR[C]=FDGHJ56D) & (set CHAR[D]=FGSG54D6) & (set CHAR[E]=JUK456JH)
(set CHAR[F]=ERE4G54S) & (set CHAR[G]=T5H64FDD) & (set CHAR[H]=RG56F41G) & (set CHAR[I]=RG45FG4D) & (set CHAR[J]=RT8564F6)
(set CHAR[K]=VCBV5C3B) & (set CHAR[L]=FD8G9G2F) & (set CHAR[M]=FDG4CVJS) & (set CHAR[N]=FG4213FG) & (set CHAR[O]=FD456GC2)
(set CHAR[P]=TH56GDF5) & (set CHAR[Q]=CV54F6GR) & (set CHAR[R]=XDF64FTS) & (set CHAR[S]=X78DG9RT) & (set CHAR[T]=TGH74SDJ)
(set CHAR[U]=BCX856DF) & (set CHAR[V]=FGH654SD) & (set CHAR[W]=45KLD45D) & (set CHAR[X]=GF2H3FG2) & (set CHAR[Y]=GFH564GF)
(set CHAR[Z]=45TG21FG) & (set CHAR[1]=D45G213D) & (set CHAR[2]=GB456DFG) & (set CHAR[3]=SDF456GF) & (set CHAR[4]=PF6F1G32)
(set CHAR[5]=FD6DFGG1) & (set CHAR[6]=56DFG54G) & (set CHAR[7]=UISG4FDG) & (set CHAR[8]=FKJH6FDG) & (set CHAR[9]=IFDGJHK6)
::Decrypt
(set CHAR[UDFMHD45]=A) & (set CHAR[H121FDGF]=B) & (set CHAR[FDGHJ56D]=C) & (set CHAR[FGSG54D6]=D) & (set CHAR[JUK456JH]=E)
(set CHAR[ERE4G54S]=F) & (set CHAR[T5H64FDD]=G) & (set CHAR[RG56F41G]=H) & (set CHAR[RG45FG4D]=I) & (set CHAR[RT8564F6]=J)
(set CHAR[VCBV5C3B]=K) & (set CHAR[FD8G9G2F]=L) & (set CHAR[FDG4CVJS]=M) & (set CHAR[FG4213FG]=N) & (set CHAR[FD456GC2]=O)
(set CHAR[TH56GDF5]=P) & (set CHAR[CV54F6GR]=Q) & (set CHAR[XDF64FTS]=R) & (set CHAR[X78DG9RT]=S) & (set CHAR[TGH74SDJ]=T)
(set CHAR[BCX856DF]=U) & (set CHAR[FGH654SD]=V) & (set CHAR[45KLD45D]=W) & (set CHAR[GF2H3FG2]=X) & (set CHAR[GFH564GF]=Y)
(set CHAR[45TG21FG]=Z) & (set CHAR[D45G213D]=1) & (set CHAR[GB456DFG]=2) & (set CHAR[SDF456GF]=3) & (set CHAR[PF6F1G32]=4)
(set CHAR[FD6DFGG1]=5) & (set CHAR[56DFG54G]=6) & (set CHAR[UISG4FDG]=7) & (set CHAR[FKJH6FDG]=8) & (set CHAR[IFDGJHK6]=9)
But it screws it's selft up. Do i need to change them to numbers only? example
set CHAR[85174187]=A
set CHAR[A]=85174187
My "changed" version does want to work ...
@echo off
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
title Decrypt
:mainmenu
(set CHAR[UDFMHD45]=A) & (set CHAR[H121FDGF]=B) & (set CHAR[FDGHJ56D]=C) & (set CHAR[FGSG54D6]=D) & (set CHAR[JUK456JH]=E)
(set CHAR[ERE4G54S]=F) & (set CHAR[T5H64FDD]=G) & (set CHAR[RG56F41G]=H) & (set CHAR[RG45FG4D]=I) & (set CHAR[RT8564F6]=J)
(set CHAR[VCBV5C3B]=K) & (set CHAR[FD8G9G2F]=L) & (set CHAR[FDG4CVJS]=M) & (set CHAR[FG4213FG]=N) & (set CHAR[FD456GC2]=O)
(set CHAR[TH56GDF5]=P) & (set CHAR[CV54F6GR]=Q) & (set CHAR[XDF64FTS]=R) & (set CHAR[X78DG9RT]=S) & (set CHAR[TGH74SDJ]=T)
(set CHAR[BCX856DF]=U) & (set CHAR[FGH654SD]=V) & (set CHAR[45KLD45D]=W) & (set CHAR[GF2H3FG2]=X) & (set CHAR[GFH564GF]=Y)
(set CHAR[45TG21FG]=Z) & (set CHAR[D45G213D]=1) & (set CHAR[GB456DFG]=2) & (set CHAR[SDF456GF]=3) & (set CHAR[PF6F1G32]=4)
(set CHAR[FD6DFGG1]=5) & (set CHAR[56DFG54G]=6) & (set CHAR[UISG4FDG]=7) & (set CHAR[FKJH6FDG]=8) & (set CHAR[IFDGJHK6]=9)
(set CHAR[HG45J6FG]=.) & (set CHAR[DF456HG4]=,)
echo Enter a string to decrypt:
set /p Decrypt=
set Decrypt2=%Decrypt%
set "DecryptOut="
:decrypt2
set char=%Decrypt2:~0,3%
set Decrypt2=%Decrypt2:~3%
set DecryptOut=%DecryptOut%!CHAR[%char%]!
if not "%Decrypt2%"=="" goto decrypt2
echo Input string: %Decrypt%
echo Output string: %DecryptOut%
echo.
pause
cls
goto mainmenu
Thanks for your time
P.S this forum is really great I love it with all your help
Re: String Encryption (encoding)
In the encrypt string will change one character (letter) to more characters (letters).
In my example from "x" to "xxx". You have change this to eight characters code "12345678".
To decrypt a string (encoding) will change the code from more characters (letters) to one character (letter).
In my example from "xxx" to "x". But you will use a eight charakter code encoder that not change to this.
Here your code with the change in red color:
@echo off
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
title Decrypt
:mainmenu
(set CHAR[UDFMHD45]=A) & (set CHAR[H121FDGF]=B) & (set CHAR[FDGHJ56D]=C) & (set CHAR[FGSG54D6]=D) & (set CHAR[JUK456JH]=E)
(set CHAR[ERE4G54S]=F) & (set CHAR[T5H64FDD]=G) & (set CHAR[RG56F41G]=H) & (set CHAR[RG45FG4D]=I) & (set CHAR[RT8564F6]=J)
(set CHAR[VCBV5C3B]=K) & (set CHAR[FD8G9G2F]=L) & (set CHAR[FDG4CVJS]=M) & (set CHAR[FG4213FG]=N) & (set CHAR[FD456GC2]=O)
(set CHAR[TH56GDF5]=P) & (set CHAR[CV54F6GR]=Q) & (set CHAR[XDF64FTS]=R) & (set CHAR[X78DG9RT]=S) & (set CHAR[TGH74SDJ]=T)
(set CHAR[BCX856DF]=U) & (set CHAR[FGH654SD]=V) & (set CHAR[45KLD45D]=W) & (set CHAR[GF2H3FG2]=X) & (set CHAR[GFH564GF]=Y)
(set CHAR[45TG21FG]=Z) & (set CHAR[D45G213D]=1) & (set CHAR[GB456DFG]=2) & (set CHAR[SDF456GF]=3) & (set CHAR[PF6F1G32]=4)
(set CHAR[FD6DFGG1]=5) & (set CHAR[56DFG54G]=6) & (set CHAR[UISG4FDG]=7) & (set CHAR[FKJH6FDG]=8) & (set CHAR[IFDGJHK6]=9)
(set CHAR[HG45J6FG]=.) & (set CHAR[DF456HG4]=,)
echo Enter a string to decrypt:
set /p Decrypt=
set Decrypt2=%Decrypt%
set "DecryptOut="
:decrypt2
set char=%Decrypt2:~0,8%
set Decrypt2=%Decrypt2:~8%
set DecryptOut=%DecryptOut%!CHAR[%char%]!
if not "%Decrypt2%"=="" goto decrypt2
echo Input string: %Decrypt%
echo Output string: %DecryptOut%
echo.
pause
cls
goto mainmenu
To understand how "%Decrypt2:~0,8%" and "%Decrypt2:~8%" will work.
The example is with one and two characters.
Please run this batch file to see the differences:
More info "SET /?"
In my example from "x" to "xxx". You have change this to eight characters code "12345678".
To decrypt a string (encoding) will change the code from more characters (letters) to one character (letter).
In my example from "xxx" to "x". But you will use a eight charakter code encoder that not change to this.
Here your code with the change in red color:
@echo off
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
title Decrypt
:mainmenu
(set CHAR[UDFMHD45]=A) & (set CHAR[H121FDGF]=B) & (set CHAR[FDGHJ56D]=C) & (set CHAR[FGSG54D6]=D) & (set CHAR[JUK456JH]=E)
(set CHAR[ERE4G54S]=F) & (set CHAR[T5H64FDD]=G) & (set CHAR[RG56F41G]=H) & (set CHAR[RG45FG4D]=I) & (set CHAR[RT8564F6]=J)
(set CHAR[VCBV5C3B]=K) & (set CHAR[FD8G9G2F]=L) & (set CHAR[FDG4CVJS]=M) & (set CHAR[FG4213FG]=N) & (set CHAR[FD456GC2]=O)
(set CHAR[TH56GDF5]=P) & (set CHAR[CV54F6GR]=Q) & (set CHAR[XDF64FTS]=R) & (set CHAR[X78DG9RT]=S) & (set CHAR[TGH74SDJ]=T)
(set CHAR[BCX856DF]=U) & (set CHAR[FGH654SD]=V) & (set CHAR[45KLD45D]=W) & (set CHAR[GF2H3FG2]=X) & (set CHAR[GFH564GF]=Y)
(set CHAR[45TG21FG]=Z) & (set CHAR[D45G213D]=1) & (set CHAR[GB456DFG]=2) & (set CHAR[SDF456GF]=3) & (set CHAR[PF6F1G32]=4)
(set CHAR[FD6DFGG1]=5) & (set CHAR[56DFG54G]=6) & (set CHAR[UISG4FDG]=7) & (set CHAR[FKJH6FDG]=8) & (set CHAR[IFDGJHK6]=9)
(set CHAR[HG45J6FG]=.) & (set CHAR[DF456HG4]=,)
echo Enter a string to decrypt:
set /p Decrypt=
set Decrypt2=%Decrypt%
set "DecryptOut="
:decrypt2
set char=%Decrypt2:~0,8%
set Decrypt2=%Decrypt2:~8%
set DecryptOut=%DecryptOut%!CHAR[%char%]!
if not "%Decrypt2%"=="" goto decrypt2
echo Input string: %Decrypt%
echo Output string: %DecryptOut%
echo.
pause
cls
goto mainmenu
To understand how "%Decrypt2:~0,8%" and "%Decrypt2:~8%" will work.
The example is with one and two characters.
Code: Select all
@echo off
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
set TestPass=123-456-7890
set TestPass2=%TestPass%
:again
set char=%TestPass2:~0,1%
set TestPass2=%TestPass2:~1%
echo "%char%" --- The first character of the string.
echo "%TestPass2%" --- The remaining string.
echo.
if not "%TestPass2%"=="" goto :again
echo ######
set TestPass=123-456-7890
set TestPass2=%TestPass%
:again2
echo "TestPass2" --- The used string.
set char=%TestPass2:~0,2%
set TestPass2=%TestPass2:~2%
echo "%char%" --- The first two characters of the string.
echo "%TestPass2%" --- The remaining string.
echo.
if not "%TestPass2%"=="" goto :again2
Please run this batch file to see the differences:
Code: Select all
@echo off
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
echo Here some example with the command SET.
echo.
set example=A simple text.
echo 00000000001111
echo 01234567890123
echo This string is used: "%example%"
echo.
echo _1_ : "%example:~3%" --- charakters from 3 to end
echo _2_ : "%example:~-3%" --- the last 3 charakters
echo _3_ : "%example:~3,7%" --- charakters from 3 to 9 (3+7-1)
echo _4_ : "%example:~3,-7%" --- charakters from 3 to 6 (7-1)
More info "SET /?"
Re: String Encryption (encoding)
Thanks for helping me :0, but i don't understand the last example Thanks again
Re: String Encryption (encoding)
Greetings all, I stumbled into this thread back in May after searching for a way to encrypt data using a batch file. I've been writing a function library of sorts for several months, I wanted to add an encryption function to the library, and this was the best example I found on the net (boy do I love DOS tips!). This thread was a great inspiration to me, however I didn't like the original or modified solution in terms of security. Not only were both ciphers mono-alphabetic, making them easily crackable like a newspaper cryptoquip, they both had the substitutions hard-coded into the batch file's source. A simple right-click -> edit would give away the encryption key! I also wanted to expand the character set that could be encoded, though I'm not great at dealing with poison like the experts around here, so I ended up with all characters working except for &, =, <, >, ^, :, " and |.
I decided to make my own cipher based on the structure above, but replacing the hard-coded substitutions with randomly generated 4-digit numbers. To do this I had to first create a psuedo-random number generator, as batch doesn't have one built in (%random% can't be seeded). I did a hack-job of porting the one from the standard C library, which doesn't exactly fit in batch's 32-bit unsigned integer space, but even with the overflowing my tests show the output to be statistically random, so it will work well enough for this purpose:
You then pass a string to my encMono function along with a key (to seed the PRNG) and it will randomly generate the substitution table (ie: A=0357, B=5196, etc...) and do the encoding. In the end the function looks like this (though both above and below I've removed many lines of input-validation and error checking for brevity):
The algorithm basically takes every character of input and if it's a lower case it adds an "A", if it's an uppercase it adds a "Z", and if it's a number or symbol it adds another of the same. So "h" = "hA", "T" = "TZ" and "4" = "44". This is so that later when decoding you can maintain case sensitivity of the password. Each of these 85 possible pairs gets mapped to and substituted with a 4-digit random number, perhaps "hA" becomes 6273 under one particular input key. There is a partner function "decMono" that does the same steps in reverse order, changing the 6273 back to a "hA" and then back to "h", but only if provided the same 32-bit input key. The algorithm is a little slow since at the beginning the random function has to be called enough times to generate 85 unique random numbers, but I felt it was worth it for the additional obfuscation.
I decided to make my own cipher based on the structure above, but replacing the hard-coded substitutions with randomly generated 4-digit numbers. To do this I had to first create a psuedo-random number generator, as batch doesn't have one built in (%random% can't be seeded). I did a hack-job of porting the one from the standard C library, which doesn't exactly fit in batch's 32-bit unsigned integer space, but even with the overflowing my tests show the output to be statistically random, so it will work well enough for this purpose:
Code: Select all
:random [<range>] [<seed>] :#returns <random> [<nextseed>] :#version 1.1
SETLOCAL
echo %~1 | find "-" >nul
IF %ERRORLEVEL%==0 (
IF "%~2"=="" set seed=
FOR /F "tokens=1,2 delims=-" %%a in ("%~1") do (
IF NOT "%%b"=="" set /a min=%%a&set /a max=%%b
IF "%%b"=="" (
set /a seed=%~1
set min=0
set max=32767
)
)
IF NOT "%~2"=="" set /a seed=%~2
) ELSE (
set /a seed=%~1
set min=0
set max=32767
)
set /a max=%max%+1-%min%
IF NOT DEFINED seed (
ENDLOCAL & set /a MarcLibReturn1=%random% %% %max% + %min% & set MarcLibErrLvl=0& set MarcLibErrMsg=& GOTO:EOF
)
set /a seed=%seed%*1103515245+12345
set MarcLibReturn2=%seed%
set /a MarcLibReturn1=(%seed%/65536)%%32768
set MarcLibReturn1=%MarcLibReturn1:-=%
set /a MarcLibReturn1=%MarcLibReturn1% %% %max% + %min%
ENDLOCAL & set /a MarcLibReturn1=%MarcLibReturn1%& set /a MarcLibReturn2=%MarcLibReturn2%& set MarcLibErrLvl=0& set MarcLibErrMsg=& GOTO:EOF
You then pass a string to my encMono function along with a key (to seed the PRNG) and it will randomly generate the substitution table (ie: A=0357, B=5196, etc...) and do the encoding. In the end the function looks like this (though both above and below I've removed many lines of input-validation and error checking for brevity):
Code: Select all
:encMono <var> <key> :#returns <null> :#version 1.2
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
set "inStr=!%~1!"
set /a seed=%~2
set "transStr="
:encm_TransStr
set transCharDone=NO
set transChar=%inStr:~0,1%
set inStr=%inStr:~1%
echo "!transChar!" | findstr /R "[abcdefghijklmnopqrstuvwxyz]" >nul
IF %ERRORLEVEL%==0 (
set "transStr=%transStr%%transChar%A"
set transCharDone=YES
)
echo "!transChar!" | findstr /R "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]" >nul
IF %ERRORLEVEL%==0 (
set "transStr=%transStr%%transChar%Z"
set transCharDone=YES
)
IF %transCharDone%==NO set "transStr=%transStr%%transChar%%transChar%"
IF NOT "%inStr%"=="" GOTO:encm_TransStr
set rand0=10000
:encm_GenRands
set dupRand=0
FOR /L %%a in (1,1,85) DO (
CALL:random 0-9999 !seed!
set seed=!MarcLibReturn2!
IF !MarcLibReturn1! LSS 10 set MarcLibReturn1=0!MarcLibReturn1!
IF !MarcLibReturn1! LSS 100 set MarcLibReturn1=0!MarcLibReturn1!
IF !MarcLibReturn1! LSS 1000 set MarcLibReturn1=0!MarcLibReturn1!
set rand%%a=!MarcLibReturn1!
FOR /L %%b in (0,1,%%a-1) DO (
IF NOT %%a==%%b (
IF !rand%%a!==!rand%%b! (
set dupRand=1
set /a seed=!seed!+1
)
)
)
IF !dupRand!==1 GOTO:encm_GenRands
)
(set CHAR[AA]=%rand1%) & (set CHAR[AZ]=%rand2%) & (set CHAR[BA]=%rand3%) & (set CHAR[BZ]=%rand4%)
(set CHAR[CA]=%rand5%) & (set CHAR[CZ]=%rand6%) & (set CHAR[DA]=%rand7%) & (set CHAR[DZ]=%rand8%)
(set CHAR[EA]=%rand9%) & (set CHAR[EZ]=%rand10%) & (set CHAR[FA]=%rand11%) & (set CHAR[FZ]=%rand12%)
(set CHAR[GA]=%rand13%) & (set CHAR[GZ]=%rand14%) & (set CHAR[HA]=%rand15%) & (set CHAR[HZ]=%rand16%)
(set CHAR[IA]=%rand17%) & (set CHAR[IZ]=%rand18%) & (set CHAR[JA]=%rand19%) & (set CHAR[JZ]=%rand20%)
(set CHAR[KA]=%rand21%) & (set CHAR[KZ]=%rand22%) & (set CHAR[LA]=%rand23%) & (set CHAR[LZ]=%rand24%)
(set CHAR[MA]=%rand25%) & (set CHAR[MZ]=%rand26%) & (set CHAR[NA]=%rand27%) & (set CHAR[NZ]=%rand28%)
(set CHAR[OA]=%rand29%) & (set CHAR[OZ]=%rand30%) & (set CHAR[PA]=%rand31%) & (set CHAR[PZ]=%rand32%)
(set CHAR[QA]=%rand33%) & (set CHAR[QZ]=%rand34%) & (set CHAR[RA]=%rand35%) & (set CHAR[RZ]=%rand36%)
(set CHAR[SA]=%rand37%) & (set CHAR[SZ]=%rand38%) & (set CHAR[TA]=%rand39%) & (set CHAR[TZ]=%rand40%)
(set CHAR[UA]=%rand41%) & (set CHAR[UZ]=%rand42%) & (set CHAR[VA]=%rand43%) & (set CHAR[VZ]=%rand44%)
(set CHAR[WA]=%rand45%) & (set CHAR[WZ]=%rand46%) & (set CHAR[XA]=%rand47%) & (set CHAR[XZ]=%rand48%)
(set CHAR[YA]=%rand49%) & (set CHAR[YZ]=%rand50%) & (set CHAR[ZA]=%rand51%) & (set CHAR[ZZ]=%rand52%)
(set CHAR[00]=%rand53%) & (set CHAR[11]=%rand54%) & (set CHAR[22]=%rand55%) & (set CHAR[33]=%rand56%)
(set CHAR[44]=%rand57%) & (set CHAR[55]=%rand58%) & (set CHAR[66]=%rand59%) & (set CHAR[77]=%rand60%)
(set CHAR[88]=%rand61%) & (set CHAR[99]=%rand62%)
(set CHAR[@@]=%rand63%) & (set CHAR[##]=%rand64%) & (set CHAR[$$]=%rand65%) & (set CHAR[**]=%rand66%)
(set CHAR[--]=%rand67%) & (set CHAR[__]=%rand68%) & (set CHAR[++]=%rand69%) & (set CHAR[,,]=%rand70%)
(set CHAR[..]=%rand71%) & (set CHAR[//]=%rand72%) & (set CHAR[??]=%rand73%) & (set CHAR[\\]=%rand74%)
(set CHAR[ ]=%rand75%) & (set CHAR[;;]=%rand76%) & (set CHAR[~~]=%rand77%) & (set CHAR[%%%%]=%rand78%)
(set CHAR[^(^(]=%rand79%) & (set CHAR[^)^)]=%rand80%) & (set CHAR[{{]=%rand81%) & (set CHAR[}}]=%rand82%)
(set CHAR[^[^[]=%rand83%) & (set CHAR[^]^]]=%rand84%) & (set CHAR[^'^']=%rand85%)
set "outStr="
:encm_NextChar
set outChar=%transStr:~0,2%
set transStr=%transStr:~2%
set outStr=%outStr%!CHAR[%outChar%]!
IF NOT "%transStr%"=="" GOTO:encm_NextChar
ENDLOCAL & set %~1=%outStr%& set MarcLibErrLvl=0& set MarcLibErrMsg=& GOTO:EOF
The algorithm basically takes every character of input and if it's a lower case it adds an "A", if it's an uppercase it adds a "Z", and if it's a number or symbol it adds another of the same. So "h" = "hA", "T" = "TZ" and "4" = "44". This is so that later when decoding you can maintain case sensitivity of the password. Each of these 85 possible pairs gets mapped to and substituted with a 4-digit random number, perhaps "hA" becomes 6273 under one particular input key. There is a partner function "decMono" that does the same steps in reverse order, changing the 6273 back to a "hA" and then back to "h", but only if provided the same 32-bit input key. The algorithm is a little slow since at the beginning the random function has to be called enough times to generate 85 unique random numbers, but I felt it was worth it for the additional obfuscation.
Code: Select all
Test string to be encoded is:
"TheWorldIsUnderAttackFromTheLivingDead.ThisIsNotATest.SeekShelterImmediately"
length of test string is:
76
Beginning deprecated mono-alphabetic cipher...
Encoding start time: 17:51:59.52
Encoding finish time: 17:52:12.29
Encoded string=05281642838573577237550212037656992836902685762476568385550266023
38433840165678864670333550272371992052816428385916300243293002476246752068183850
16576561517052816420024369099283690528172373384660205288385369033841517451383858
3856467451316428385120333848385550299281992199283857656002401653384838512032273
Decoding start time: 17:52:12.29
Decoding finish time: 17:52:23.00
Decoded string=TheWorldIsUnderAttackFromTheLivingDead.ThisIsNotATest.SeekShelter
Immediately
Re: String Encryption (encoding)
Apologies in advance for the double post, but in case it wasn't obvious from the name "encMono" and the word "deprecated" in the test results, there is more to this story with my home-made ciphers.
After the encMono/decMono pair I decided to improve the cipher's security by making it poly-alphabetic. Instead of swapping "A" for a random 4-digit number, the encPoly function swaps "A" for another randomly chosen symbol, such as "(" or "h". The same case-sensitivity and lack of poison character handling applies, but it's a faster function than encMono up until about 150-200 character long strings, then it reaches a breakeven point and becomes slower due to all the calls to Random.
I still wasn't satisfied with this cipher so I decided to code one that doesn't rely on the random number generator. I implemented a Vigenere cipher function that can be run in either "normal" mode or "running" mode as follows:
1.) In normal mode, the cipher takes a keyword or keyphrase and rotates each character in the input string a number of places around the character set based on the key. So for example with the message "Hello World" and the keyword "Donut" you might rotate the "H" 4 places (D is the 4th letter of the alphabet) and come up with "L".
2.) In running mode we go back to using the PRNG from before, and based on an input key we generate the keyphrase as a set of random symbols the same length as the message. The running Vigenere then emulates a one-time pad cipher, making it unbreakable short of brute forcing the (admittedly tiny) 32-bit key value. With all the input validation and error checking removed this Vigenere function looks like this:
As you can see this function doesn't need a partner function as it can either encode or decode based on an input parameter "enc" or "dec". It's also much much faster than the previous Mono or Poly ciphers, regardless of whether you run it in normal mode or running mode. Here's the test results showing 4 second processing vs. the prior ciphers' 7-13 seconds:
After the encMono/decMono pair I decided to improve the cipher's security by making it poly-alphabetic. Instead of swapping "A" for a random 4-digit number, the encPoly function swaps "A" for another randomly chosen symbol, such as "(" or "h". The same case-sensitivity and lack of poison character handling applies, but it's a faster function than encMono up until about 150-200 character long strings, then it reaches a breakeven point and becomes slower due to all the calls to Random.
Code: Select all
Beginning poly-alphabetic cipher...
Encoding start time: 17:52:23.01
Encoding finish time: 17:52:32.42
Encoded string=6BZAHHLB+$@I(J]HH9_I/5KSSBTKU-8YR@E01EW-NCMX,'D/RESCP2ZVRB3IIOE-M
6RCY79XU04ER* {7J-J)(-+ A\OYT#$$SW?$L4E5}]87-VYPS6W(~K{[3@H,FK3E[ M+[ZX2H-G1AKC
*@$BB(7
Decoding start time: 17:52:32.42
Decoding finish time: 17:52:39.66
Decoded string=TheWorldIsUnderAttackFromTheLivingDead.ThisIsNotATest.SeekShelter
Immediately
I still wasn't satisfied with this cipher so I decided to code one that doesn't rely on the random number generator. I implemented a Vigenere cipher function that can be run in either "normal" mode or "running" mode as follows:
1.) In normal mode, the cipher takes a keyword or keyphrase and rotates each character in the input string a number of places around the character set based on the key. So for example with the message "Hello World" and the keyword "Donut" you might rotate the "H" 4 places (D is the 4th letter of the alphabet) and come up with "L".
2.) In running mode we go back to using the PRNG from before, and based on an input key we generate the keyphrase as a set of random symbols the same length as the message. The running Vigenere then emulates a one-time pad cipher, making it unbreakable short of brute forcing the (admittedly tiny) 32-bit key value. With all the input validation and error checking removed this Vigenere function looks like this:
Code: Select all
:vigenere <var> <key> <enc/dec> [<run>] :#returns <null> :#version 1.0
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
set encdec=%~3
set encdec=%encdec:~0,3%
IF /I "!encdec!"=="enc" (
set "sign1=+"
set "sign2=-"
set "arg1=GTR 85"
set "arg2=-85"
)
IF /I "!encdec!"=="dec" (
set "sign1=-"
set "sign2=+"
set "arg1=LSS 1"
set "arg2=+85"
)
set "inStr=!%~1!"
set "key=%~2"
(set CHAR[AA]=1) & (set CHAR[AZ]=2) & (set CHAR[BA]=3) & (set CHAR[BZ]=4) & (set CHAR[CA]=5)
(set CHAR[CZ]=6) & (set CHAR[DA]=7) & (set CHAR[DZ]=8) & (set CHAR[EA]=9) & (set CHAR[EZ]=10)
(set CHAR[FA]=11) & (set CHAR[FZ]=12) & (set CHAR[GA]=13) & (set CHAR[GZ]=14) & (set CHAR[HA]=15)
(set CHAR[HZ]=16) & (set CHAR[IA]=17) & (set CHAR[IZ]=18) & (set CHAR[JA]=19) & (set CHAR[JZ]=20)
(set CHAR[KA]=21) & (set CHAR[KZ]=22) & (set CHAR[LA]=23) & (set CHAR[LZ]=24) & (set CHAR[MA]=25)
(set CHAR[MZ]=26) & (set CHAR[NA]=27) & (set CHAR[NZ]=28) & (set CHAR[OA]=29) & (set CHAR[OZ]=30)
(set CHAR[PA]=31) & (set CHAR[PZ]=32) & (set CHAR[QA]=33) & (set CHAR[QZ]=34) & (set CHAR[RA]=35)
(set CHAR[RZ]=36) & (set CHAR[SA]=37) & (set CHAR[SZ]=38) & (set CHAR[TA]=39) & (set CHAR[TZ]=40)
(set CHAR[UA]=41) & (set CHAR[UZ]=42) & (set CHAR[VA]=43) & (set CHAR[VZ]=44) & (set CHAR[WA]=45)
(set CHAR[WZ]=46) & (set CHAR[XA]=47) & (set CHAR[XZ]=48) & (set CHAR[YA]=49) & (set CHAR[YZ]=50)
(set CHAR[ZA]=51) & (set CHAR[ZZ]=52) & (set CHAR[0A]=53) & (set CHAR[1A]=54) & (set CHAR[2A]=55)
(set CHAR[3A]=56) & (set CHAR[4A]=57) & (set CHAR[5A]=58) & (set CHAR[6A]=59) & (set CHAR[7A]=60)
(set CHAR[8A]=61) & (set CHAR[9A]=62) & (set CHAR[@A]=63) & (set CHAR[#A]=64) & (set CHAR[$A]=65)
(set CHAR[*A]=66) & (set CHAR[-A]=67) & (set CHAR[_A]=68) & (set CHAR[+A]=69) & (set CHAR[,A]=70)
(set CHAR[.A]=71) & (set CHAR[/A]=72) & (set CHAR[?A]=73) & (set CHAR[\A]=74) & (set CHAR[ A]=75)
(set CHAR[;A]=76) & (set CHAR[~A]=77) & (set CHAR[%%A]=78) & (set CHAR[^(A]=79) & (set CHAR[^)A]=80)
(set CHAR[{A]=81) & (set CHAR[}A]=82) & (set CHAR[^[A]=83) & (set CHAR[^]A]=84) & (set CHAR[^'A]=85)
(set CHAR[1]=aa) & (set CHAR[2]=Az) & (set CHAR[3]=bb) & (set CHAR[4]=Bz) & (set CHAR[5]=cc)
(set CHAR[6]=Cz) & (set CHAR[7]=dd) & (set CHAR[8]=Dz) & (set CHAR[9]=ee) & (set CHAR[10]=Ez)
(set CHAR[11]=ff) & (set CHAR[12]=Fz) & (set CHAR[13]=gg) & (set CHAR[14]=Gz) & (set CHAR[15]=hh)
(set CHAR[16]=Hz) & (set CHAR[17]=ii) & (set CHAR[18]=Iz) & (set CHAR[19]=jj) & (set CHAR[20]=Jz)
(set CHAR[21]=kk) & (set CHAR[22]=Kz) & (set CHAR[23]=ll) & (set CHAR[24]=Lz) & (set CHAR[25]=mm)
(set CHAR[26]=Mz) & (set CHAR[27]=nn) & (set CHAR[28]=Nz) & (set CHAR[29]=oo) & (set CHAR[30]=Oz)
(set CHAR[31]=pp) & (set CHAR[32]=Pz) & (set CHAR[33]=qq) & (set CHAR[34]=Qz) & (set CHAR[35]=rr)
(set CHAR[36]=Rz) & (set CHAR[37]=ss) & (set CHAR[38]=Sz) & (set CHAR[39]=tt) & (set CHAR[40]=Tz)
(set CHAR[41]=uu) & (set CHAR[42]=Uz) & (set CHAR[43]=vv) & (set CHAR[44]=Vz) & (set CHAR[45]=ww)
(set CHAR[46]=Wz) & (set CHAR[47]=xx) & (set CHAR[48]=Xz) & (set CHAR[49]=yy) & (set CHAR[50]=Yz)
(set CHAR[51]=zz) & (set CHAR[52]=Za) & (set CHAR[53]=0a) & (set CHAR[54]=1a) & (set CHAR[55]=2a)
(set CHAR[56]=3a) & (set CHAR[57]=4a) & (set CHAR[58]=5a) & (set CHAR[59]=6a) & (set CHAR[60]=7a)
(set CHAR[61]=8a) & (set CHAR[62]=9a) & (set CHAR[63]=@a) & (set CHAR[64]=#a) & (set CHAR[65]=$a)
(set CHAR[66]=*a) & (set CHAR[67]=-a) & (set CHAR[68]=_a) & (set CHAR[69]=+a) & (set CHAR[70]=,a)
(set CHAR[71]=.a) & (set CHAR[72]=/a) & (set CHAR[73]=?a) & (set CHAR[74]=\a) & (set CHAR[75]= a)
(set CHAR[76]=;a) & (set CHAR[77]=~a) & (set CHAR[78]=%%a) & (set CHAR[79]=^(a) & (set CHAR[80]=^)a)
(set CHAR[81]={a) & (set CHAR[82]=}a) & (set CHAR[83]=[a) & (set CHAR[84]=]a) & (set CHAR[85]='a)
set running=NO & IF /I "%~4"=="run" set running=YES
set "keyExp="
CALL:strLen inStr
set inStrLen=!MarcLibReturn1!
IF %running%==YES (
set /A key=%~2
set keyLen=0
:vig_runKey
CALL:random 1-85 !key!
set key=!MarcLibReturn2!
set keyVal=!MarcLibReturn1!
set "keyVal=!CHAR[%keyval%]!"
IF NOT "%keyVal%"=="" (
set "keyExp=%keyExp%%keyVal:~0,1%"
set /A keyLen=%keyLen%+1
)
IF NOT "%keylen%"=="%inStrLen%" GOTO:vig_runKey
)
IF %running%==NO (
CALL:strLen key
set keyLen=!MarcLibReturn1!
set /A mult=inStrLen/keyLen+1
FOR /L %%a IN (1,1,!mult!) DO (
set "keyExp=!keyExp!!key!"
)
CALL:leftCol keyExp !inStrLen!
)
set "outStr="
:vig_ProcStr
set thisChar=%inStr:~0,1%
set inStr=%inStr:~1%
echo "!thisChar!" | findstr /R "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]" >nul
IF %ERRORLEVEL%==0 (
set "thisVal=!CHAR[%thisChar%Z]!"
) ELSE (
set "thisVal=!CHAR[%thisChar%A]!"
)
set thisKey=%keyExp:~0,1%
set keyExp=%keyExp:~1%
echo "!thisKey!" | findstr /R "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]" >nul
IF %ERRORLEVEL%==0 (
set "keyVal=!CHAR[%thisKey%Z]!"
) ELSE (
set "keyVal=!CHAR[%thisKey%A]!"
)
set /A sum=!thisVal!%sign1%!keyVal!%sign2%1
IF !sum! %arg1% set /A sum=!sum!%arg2%
set outChar=!CHAR[%sum%]!
set outStr=%outStr%!outChar:~0,1!
IF NOT "%inStr%"=="" GOTO:vig_ProcStr
ENDLOCAL & set %~1=%outStr%& set MarcLibErrLvl=0& set MarcLibErrMsg=& GOTO:EOF
As you can see this function doesn't need a partner function as it can either encode or decode based on an input parameter "enc" or "dec". It's also much much faster than the previous Mono or Poly ciphers, regardless of whether you run it in normal mode or running mode. Here's the test results showing 4 second processing vs. the prior ciphers' 7-13 seconds:
Code: Select all
Beginning Vigenere cipher...
Encoding start time: 17:52:39.67
Encoding finish time: 17:52:43.67
Encoded string=xVRA_UZQ6;y3QY\e_*UVNu9,#xVR$3YW11xHOQn(KW#6;r5*v(H**n~HSX(1HZ*Y\
m1ZYWLO*Y93
Decoding start time: 17:52:43.67
Decoding finish time: 17:52:47.65
Decoded string=TheWorldIsUnderAttackFromTheLivingDead.ThisIsNotATest.SeekShelter
Immediately
Beginning Vigenere cipher in running mode...
Encoding start time: 17:52:47.66
Encoding finish time: 17:52:51.65
Encoded string=k_@H'dB7/dg]?9B4Ef-5 -CaCD+#)\L,{_#*-7TE.\I.C['f_D@dEvj9@;e/ ;D#C
C%@9?5J9~j
Decoding start time: 17:52:51.66
Decoding finish time: 17:52:55.66
Decoded string=TheWorldIsUnderAttackFromTheLivingDead.ThisIsNotATest.SeekShelter
Immediately
Re: String Encryption (encoding)
I promise I'll keep this third post short, and I won't post any more code, but I just wanted to mention the culmination of all this effort generating cryptographic ciphers. After my Vigenere function I looked around for something I could code that would be a little more secure. Something that could overcome the 32-bit limitation of the batch environment and the resulting poor security against brute force.
What I ended up with I think is really neat. After a week or two of studying the details of the German Enigma I and its construction, I now have a batch function "Enigma" that perfectly simulates the encryption machine's behavior. I've tested it against other online simulators and I get the same results every time (after dozens of frustrating failures along the way).
My machine allows you to choose three of five authentic rotors to install, one of two authentic reflectors, as well as starting letter positions and ring positions on all three rotors. Finally, anywhere from 0 to 13 wire connections can be made on the plugboard, and the function is bi-directional like Vigenere, it does not need a partner to decode. The only "non-authentic" thing about it is that if numbers or special characters are found in the input string my function passes them through without encoding. The actual machine only had letter keys, so you could never input a number or symbol, but I didn't want my machine to just drop those characters on the floor so it passes them into the output string to preserve them.
The function definition looks like this:
and here is a test of it running:
As you can see this is the only cipher I made that does not preserve letter case, however that's the price to pay for authenticity, the original machine only worked in uppercase. In any case, given the number of possible starting position combinations that could be input to the machine, I calculated that it provides an approximate equivalent of 84 bits of security against brute forcing. It also doesn't use my hacked up PRNG so there's no attack vector through that weaker function. I'm pretty thrilled about what I accomplished given the limitations of the batch environment, and if anyone is interested in more info or additional source code for any of the functions I talked about please ask!
I've been lurking here on DOS tips for years borrowing ideas and code bits from people, so I thought this would be a good opportunity to give a little something back. Thank you all!
What I ended up with I think is really neat. After a week or two of studying the details of the German Enigma I and its construction, I now have a batch function "Enigma" that perfectly simulates the encryption machine's behavior. I've tested it against other online simulators and I get the same results every time (after dozens of frustrating failures along the way).
My machine allows you to choose three of five authentic rotors to install, one of two authentic reflectors, as well as starting letter positions and ring positions on all three rotors. Finally, anywhere from 0 to 13 wire connections can be made on the plugboard, and the function is bi-directional like Vigenere, it does not need a partner to decode. The only "non-authentic" thing about it is that if numbers or special characters are found in the input string my function passes them through without encoding. The actual machine only had letter keys, so you could never input a number or symbol, but I didn't want my machine to just drop those characters on the floor so it passes them into the output string to preserve them.
The function definition looks like this:
Code: Select all
:enigma <var> <rotors> [<key>] [<rings>] [<plugmap>] :#returns <null> :#version 1.2
and here is a test of it running:
Code: Select all
Beginning Enigma cipher...
Encoding start time: 17:52:55.67
Encoding finish time: 17:53:14.00
Encoded string=FILYMEIFBKIEULMMWELVWDQHDKUXOMFZHQMZLC.WAYGXYHCETRAXB.LGXFGPTPRMJ
LVPTVQOZZMM
Decoding start time: 17:53:14.00
Decoding finish time: 17:53:32.37
Decoded string=THEWORLDISUNDERATTACKFROMTHELIVINGDEAD.THISISNOTATEST.SEEKSHELTER
IMMEDIATELY
As you can see this is the only cipher I made that does not preserve letter case, however that's the price to pay for authenticity, the original machine only worked in uppercase. In any case, given the number of possible starting position combinations that could be input to the machine, I calculated that it provides an approximate equivalent of 84 bits of security against brute forcing. It also doesn't use my hacked up PRNG so there's no attack vector through that weaker function. I'm pretty thrilled about what I accomplished given the limitations of the batch environment, and if anyone is interested in more info or additional source code for any of the functions I talked about please ask!
I've been lurking here on DOS tips for years borrowing ideas and code bits from people, so I thought this would be a good opportunity to give a little something back. Thank you all!
Re: String Encryption (encoding)
Greetings! I'll let those who have an interest in this field analyse your code.
It looks convoluted... nice to see you post though.
It looks convoluted... nice to see you post though.
Re: String Encryption (encoding)
I admit it's definitely convoluted. It's all fresh off the presses in the "proof of concept" / "hooray everything works" stage. I was so surprised that I couldn't find any real good examples of batch file cryptography that I figured I'd code up a few to share. I haven't had the time to go back through and clean up or optimize anything, for example a lot of GOTO:LABEL logic could easily be replaced by "FOR 1-> strLen DO". That, and I'm just not that good at optimization in the first place. I'm constantly picking my jaw up off the floor with the tricks the guys around here use in macro code. Some day I'm going to delve into that stuff and try to convert my function library into a bunch of macros...
In any case, I just wanted to get something posted since this thread was already over 2 months old and looking pretty dead
In any case, I just wanted to get something posted since this thread was already over 2 months old and looking pretty dead
Re: String Encryption (encoding)
Magialisk wrote:I admit it's definitely convoluted. It's all fresh off the presses in the "proof of concept" / "hooray everything works" stage. I was so surprised that I couldn't find any real good examples of batch file cryptography that I figured I'd code up a few to share. I haven't had the time to go back through and clean up or optimize anything, for example a lot of GOTO:LABEL logic could easily be replaced by "FOR 1-> strLen DO". That, and I'm just not that good at optimization in the first place. I'm constantly picking my jaw up off the floor with the tricks the guys around here use in macro code. Some day I'm going to delve into that stuff and try to convert my function library into a bunch of macros...
In any case, I just wanted to get something posted since this thread was already over 2 months old and looking pretty dead
I'll be happy to throw in some additional examples in this area. Don't know how much interest there is in this particular topic.
I have also created an 'enigma-like' batch file that will encrypt or decrypt entire files, not just strings. It does not attempt to exactly recreate the original enigma machine, but it does provide a similar level of encryption. It basically loads 26 'rotors' or translation tables into memory and utilizes the numeric values of the letters in the provided key phrase to determine which rotor is to be used for the encryption or decryption of each character of the text that is being processed.
The batch file itself is simple, because the process is table-driven. It does not, however, handle a few of the "poison characters" that are the bane of CMD coding. (I didn't design this process to able to encode or decode batch files. At some point, if I have time, I will write pre- and post-processor routines to handle the 'poison' characters.)
For example, the table for row10 looks like
Code: Select all
A.K a.k B.L b.l C.M c.m . . . X.H x.h Y.I y.i Z.J z.j
The tables were all built by this batch file:
Code: Select all
@echo off
setlocal enabledelayedexpansion
set "range=%*"
if not defined range set "range=2,1,24"
set range=%range%,1,%range%
set "line1=AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
for /l %%n in (%range%) do call :build_tbl %%n
goto :EOF
:build_tbl
set /a spec = %1 + 1, tbl = %1
type nul>%temp%\rot%tbl%.dat
set /a ptr = spec * 2 - 2
set "line2=!line1:~%ptr%!!line1!"
set "line2=%line2:~0,52%"
set /a ctr = 0 , lctr = 1
for /l %%a in (1,1,52) do call :pair %%a
for /l %%a in (1,1,%lctr%) do echo/!lineout%%a!>>%temp%\rot%tbl%.dat
for /f "tokens=1 delims==" %%i in ('set lineout') do set "%%i="
echo/
echo/···················%temp%\rot%tbl%.dat····················
type %temp%\rot%tbl%.dat
goto :EOF
:pair
set /a ptr = %1 - 1
set /a ctr += 1
if %ctr% equ 14 set /a lctr += 1 & set /a ctr = 1
set FROM=!line1:~%ptr%,1!
set TO=!line2:~%ptr%,1!
set "entry=%FROM%.%TO% "
set "lineout%lctr%=!lineout%lctr%!%entry%"
goto :EOF
Here's a sample table generation:
Code: Select all
------------screen capture Win 7 Pro-------------
C:\Users\Phil>makerotable 10
···················c:\temp\rot10.dat····················
A.K a.k B.L b.l C.M c.m D.N d.n E.O e.o F.P f.p G.Q
g.q H.R h.r I.S i.s J.T j.t K.U k.u L.V l.v M.W m.w
N.X n.x O.Y o.y P.Z p.z Q.A q.a R.B r.b S.C s.c T.D
t.d U.E u.e V.F v.f W.G w.g X.H x.h Y.I y.i Z.J z.j
-------------end screen capture------------------
So here is a sample run of my Enigma4:
Code: Select all
-----------------------begin screen capture Win 7 Pro----------------------
C:\PXRWORK>type project1.txt
\cmd\DEMO\findnewerfiles.001
\cmd\DEMO\findnewerfiles.cmd
\cmd\DEMO\FindOldFiles.cmd
\cmd\DEMO\newfiles.001
\cmd\DEMO\newfiles.002
\cmd\DEMO\newfiles.cmd
\cmd\DEMO\SortableFileList.cmd
\cmd\DEMO\xforfile.cmd
\cmd\TEST\FINDNEWfiles.001
\cmd\TEST\FINDNEWfiles.cmd
\cmd\TEST\GetFileDateTime.cmd
C:\PXRWORK>enigma4
Enter function: E(ncrypt) or D(ecrypt) ==> e
Enter a sentence or phrase for the key ==> one eyed one horned flying purple people eater
Enter name of file to be ENcrypted ======> project1.txt
\qri\IIBC\nxfrsicqqowsun.001
\hbt\IJNI\xxbisdbigtnttk.hqj
\bvr\TZEE\KyssExiKjfjk.qri
\hqs\IMBG\sicrhusz.001
\hci\TQRT\hjouwqjr.002
\kbv\IISA\wsdvddue.srs
\ori\XJED\XtqyeqzjNxdsQmyf.lak
\xet\IURD\jktszndt.hrc
\gbr\BTKH\JOZCWSDvddue.001
\ori\NJKI\KNMIRTKkqawg.gsp
\lak\OWIF\WjiVuqjEuywIwrj.hqs
Do you want to save this output? ========> y
Please enter name of file to save =======> project1.enc
C:\PXRWORK>enigma4
Enter function: E(ncrypt) or D(ecrypt) ==> d
Enter a sentence or phrase for the key ==> one eyed one horned flying purple people eater
Enter name of file to be DEcrypted ======> project1.enc
\cmd\DEMO\findnewerfiles.001
\cmd\DEMO\findnewerfiles.cmd
\cmd\DEMO\FindOldFiles.cmd
\cmd\DEMO\newfiles.001
\cmd\DEMO\newfiles.002
\cmd\DEMO\newfiles.cmd
\cmd\DEMO\SortableFileList.cmd
\cmd\DEMO\xforfile.cmd
\cmd\TEST\FINDNEWfiles.001
\cmd\TEST\FINDNEWfiles.cmd
\cmd\TEST\GetFileDateTime.cmd
Do you want to save this output? ========> n
--------------------end screen capture----------------------
So to keep this post from becoming overly long, I will post the actual source for my
Enigma batch in a follow-up if there is any interest. There are a few subtle differences
in that it uses 'rotor' tables that are single-line records and loads them via 'set /p'
commands, etc.; so if I posted it now without further elaboration it might not work
without a tweak or something.
Anyhow, I for one am interested in this topic. 3110?
--
Phil Robyn
Univ. of Calif. Berkeley (retired)
ude tod yelekreb ta nyborp
Re: String Encryption (encoding)
That's really nice Phil! My Enigma function (or any of my other ciphers) could theoretically encode entire files if they were run in a for loop to extract every line as an input string. Of course the same disclaimers would apply about not handling poison characters, and with my Enigma specifically, any numbers or symbols would pass straight through since I designed it for authenticity. In my case the Vigenere would probably be a better solution for whole-file encryption, as it's a much faster cipher and handles non-alpha characters. I'm definitely into this stuff as a hobby and would love to see more of your code.
I really like the way you auto-generate your rotor configurations as well because it ties into another project I was considering tackling... See the real weakness of the Enigma machines is that the Germans only produced 3, and then 5, and then I think 8 different types of rotors. Out of the billions of possible rotors they could have produced, the allies only had to guess which 3 of 5, etc. they were using. The machine in theory could provide around 10^114 combinations but as deployed and used it was closer to 10^23 in practice (I'm pulling those numbers from memory, exponents might be off by a few)
As such, I had an idea for an Uber-Enigma function that would use a PRNG to randomly create non-authentic rotors and reflectors. Of course while we're going non-authentic I'd allow it to process lower case, numbers and symbols as well like the other ciphers, so each rotor would have more like 85 positions instead of 26. If I implemented this option, instead of guessing which 1 of 2 reflectors and 3 of 5 rotors were used (120 combinations, about 2^7) someone would have to guess the 32-bit key. On top of that ~25-bit security bump, the original 26^6 factor from the unknown starting positions and rings becomes more like 85^6 due to the extended character set, bumping another 10 bits or so. Finally, if you went hog wild and opened up the plugboard to all those other character pairs too.... yikes! I tried to calculate the possible plugboard combinations for 84 characters on an online calculator and it came back ~10^102 by itself. If that's correct, and you add in everything else above the whole machine shakes out around 10^124, or ~412-bits worth. Absolutely insane for a batch file, too bad usability would be down the tubes
I really like the way you auto-generate your rotor configurations as well because it ties into another project I was considering tackling... See the real weakness of the Enigma machines is that the Germans only produced 3, and then 5, and then I think 8 different types of rotors. Out of the billions of possible rotors they could have produced, the allies only had to guess which 3 of 5, etc. they were using. The machine in theory could provide around 10^114 combinations but as deployed and used it was closer to 10^23 in practice (I'm pulling those numbers from memory, exponents might be off by a few)
As such, I had an idea for an Uber-Enigma function that would use a PRNG to randomly create non-authentic rotors and reflectors. Of course while we're going non-authentic I'd allow it to process lower case, numbers and symbols as well like the other ciphers, so each rotor would have more like 85 positions instead of 26. If I implemented this option, instead of guessing which 1 of 2 reflectors and 3 of 5 rotors were used (120 combinations, about 2^7) someone would have to guess the 32-bit key. On top of that ~25-bit security bump, the original 26^6 factor from the unknown starting positions and rings becomes more like 85^6 due to the extended character set, bumping another 10 bits or so. Finally, if you went hog wild and opened up the plugboard to all those other character pairs too.... yikes! I tried to calculate the possible plugboard combinations for 84 characters on an online calculator and it came back ~10^102 by itself. If that's correct, and you add in everything else above the whole machine shakes out around 10^124, or ~412-bits worth. Absolutely insane for a batch file, too bad usability would be down the tubes
Re: String Encryption (encoding)
OK, I took a look at my Enigma4 and I actually need to post two batch files.
And I see that I am using one of my more general-purpose 'xlate' routines to process the key, so I've got to include that one as well.
The LetterValues table looks like
Yeah, you are absolutely right! With the general-purpose 'xlate' batch that I included above, in other contexts, I use many other tables besides just alphabetic to alphabetic. There is no reason (other than to facilitate ease of understanding) to limit the contents of the rotors used to just alphabetic characters. Here's an example:
Also, there is no reason to limit the number of 'rotors' or translation tables. Just remember, for every 'IN' table (see example above) you've got to have a corresponding 'OUT'.
The processing of numbers using this particular technique is worthy of another thread all by itself.
--
Phil Robyn
Univ. of California, Berkeley
ude tod yelekreb ta nyborp
Code: Select all
≡≡≡≡≡begin c:\Cmd\test\enigma4.cmd≡≡≡≡≡
001. @echo off
002. setlocal enabledelayedexpansion
003.
004. :function
005. set /p op="Enter function: E(ncrypt) or D(ecrypt) ==> "
006. if not defined op goto :EOF
007. set op=%op:~0,1%
008. echo .D.E. | findstr /i /c:".%op%." >nul
009. if %errorlevel%==0 goto :CONTINUE
010. echo/ Invalid function. Please enter E or D.
011. goto :function
012.
013. :CONTINUE
014.
015. if /i "%op%"=="E" set op=EN
016. if /i "%op%"=="D" set op=DE
017. call :getkey
018. if not defined seq goto :EOF
019. call :load
020. :getfile
021. set /p file="Enter name of file to be %op%crypted ======> "
022. if not defined file goto :EOF
023. if not exist %file% (
024. echo/%file% not found - press ^<Enter^> to QUIT.
025. goto :getfile
026. )
027. set /a lctr = 0
028. for /f "tokens=1* delims=:" %%a in (
029. 'findstr /n /v /i "NaIlAdEpIuQsEs" %file%'
030. ) do (
031. set "recnum=%%a"
032. set /a lctr += 1
033. set "LINE=%%b"
034. if defined LINE (
035. call :MAIN
036. set "line!lctr!=rec!recnum!\\!result!"
037. ) else ( set "line!lctr!=rec!recnum!\\$BLNKLINE$" )
038. )
039.
040. type nul>%temp%\%~n0.out
041.
042. for /l %%i in (1,1,%lctr%) do (
043. set lineout=!line%%i!
044. set lineout=!lineout:*\\=!
045. set lineout=!lineout:$BLNKLINE$=!
046. echo/!lineout!>>%temp%\%~n0.out
047. )
048. type %temp%\%~n0.out
049. call :savefile
050. goto :EOF
051.
052. :MAIN
053. set "result="
054.
055. :CHAR
056. call :click
057. set "char=%LINE:~0,1%"
058. set "LINE=%LINE:~1%"
059. set "out="
060. for %%A in ( !tbl%X%! ) do (
061. if "%char%"=="%%~nA" set "out=%%~xA"
062. )
063. if defined out (
064. set "out=%out:~1%"
065. ) else (
066. set "out=%char%"
067. )
068. set "result=!result!!out!"
069. if defined LINE goto :CHAR
070. goto :EOF
071.
072. :click
073. set /a X = 1%seq:~0,2% - 100
074. set "seq=%seq:* =% %seq:~0,2%"
075. goto :EOF
076.
077. :load
078. for /l %%i in (1,1,26) do (
079. set /p tbl%%i=<\cmd\data\Row%%i.tbl
080. )
081. goto :EOF
082.
083. :getkey
084. set /p seq="Enter a sentence or phrase for the key ==> "
085. set "seq=%seq: =%"
086. set "s2="
087. :nxt
088. set "s2=!s2!!seq:~0,1!_"
089. set "seq=!seq:~1!"
090. if defined seq goto nxt
091. call xlate tbl=LetterValues !s2!
092. set "seq=!xlate:_= !"
093. set "seq=%seq:~0,-1%"
094. if /i "%op%"=="EN" goto :EOF
095. set "s2="
096. :conv
097. for /f "tokens=1* delims= " %%a in (
098. "!seq!"
099. ) do (
100. set /a s1 = 126 - 1%%a
101. set "seq=%%b"
102. set "s1=0!s1!" & set "s1=!s1:~-2!"
103. set "s2=!s2!!s1! "
104. )
105. if defined seq goto :conv
106. set "seq=!s2!"
107. set "seq=!seq:~0,-1!"
108. goto :EOF
109.
110. :savefile
111.
112. set /p ansr="Do you want to save this output? ========> "
113. if not defined ansr goto :EOF
114. set ansr=%ansr:~0,1%
115. echo .y.Y. | findstr /i /c:".%ansr%." >nul
116. if %errorlevel% neq 0 goto :EOF
117. :getname
118. set /p name="Please enter name of file to save =======> "
119. if not defined name goto :savefile
120. move %temp%\%~n0.out %name% > nul
121. goto :EOF
≡≡≡≡≡end c:\Cmd\test\enigma4.cmd≡≡≡≡≡
And I see that I am using one of my more general-purpose 'xlate' routines to process the key, so I've got to include that one as well.
Code: Select all
≡≡≡≡≡begin c:\Cmd\test\xlate.cmd≡≡≡≡≡
01. :\cmd\test\xlate.cmd
02. @echo off
03. ::. syntax %~n0 tbl=^<tblname^> one line of input ......
04. setlocal enabledelayedexpansion
05. set "disp="
06. set "input=%*"
07. for /f "tokens=1,2,* delims== " %%a in (
08. "%input%"
09. ) do (
10. set "tbl=%%a"
11. set "tblname=%%b"
12. if "!tbl:~0,1!" equ "." (
13. set "disp=yes"
14. set tbl=!tbl:~1!
15. )
16. if /i "!tbl!" neq "TBL" (
17. set "datatable=RotB"
18. ) else (
19. set "datatable=%%b"
20. set "input=%%c"
21. )
22. )
23. for /f "tokens=*" %%a in (
24. 'type \cmd\data\%datatable%.dat'
25. ) do (set table=!table! %%a)
26. set type=%datatable:~0,3%
27. :main
28. if /i "!type!" neq "HEX" (
29. set char=!input:~0,1!
30. set input=!input:~1!
31. ) else (
32. set char=!input:~0,2!
33. set input=!input:~2!
34. if "!char:~0,1!" equ " " (
35. set "x=!char:~1,1!"
36. set "char=!x!!input:~0,1!"
37. set "input=!input:~1!"
38. set "result=!result! "
39. )
40. )
41. set "out="
42. for %%A in (%table%) do (
43. if "%char%"=="%%~nA" set "out=%%~xA"
44. )
45. if defined out (
46. set "out=%out:~1%"
47. ) else (
48. set "out=%char%"
49. )
50. set "result=!result!!out!"
51. if defined input goto :main
52. if /i "%disp%" equ "YES" echo/%result%
53. endlocal&set "%~n0=%result%"&goto :EOF
≡≡≡≡≡end c:\Cmd\test\xlate.cmd≡≡≡≡≡
The LetterValues table looks like
Code: Select all
c:\Pxrwork>type \cmd\data\LetterValues.dat
A.01 a.01 B.02 b.02
C.03 c.03 D.04 d.04
E.05 e.05 F.06 f.06
G.07 g.07 H.08 h.08
I.09 i.09 J.10 j.10
K.11 k.11 L.12 l.12
M.13 m.13 N.14 n.14
O.15 o.15 P.16 p.16
Q.17 q.17 R.18 r.18
S.19 s.19 T.20 t.20
U.21 u.21 V.22 v.22
W.23 w.23 X.24 x.24
Y.25 y.25 Z.26 z.26
I really like the way you auto-generate your rotor configurations as well because it ties into another project I was considering tackling... See the real weakness of the Enigma machines is that the Germans only produced 3, and then 5, and then I think 8 different types of rotors. Out of the billions of possible rotors they could have produced, the allies only had to guess which 3 of 5, etc. they were using. The machine in theory could provide around 10^114 combinations but as deployed and used it was closer to 10^23 in practice (I'm pulling those numbers from memory, exponents might be off by a few)
Yeah, you are absolutely right! With the general-purpose 'xlate' batch that I included above, in other contexts, I use many other tables besides just alphabetic to alphabetic. There is no reason (other than to facilitate ease of understanding) to limit the contents of the rotors used to just alphabetic characters. Here's an example:
Code: Select all
-------------screen capture--------------
C:\PXRWORK>xlate .tbl=Jin The quick brown fox jumps over the lazy dog
»¶Å ─☼è¢Ç º⌂↨ÄÖ ▓↨ê Æ☼«¿♠ ↨ÿÅ⌂ ∟¶Å #ïàì í↨£
C:\PXRWORK>set xlate
xlate=»¶Å ─☼è¢Ç º⌂↨ÄÖ ▓↨ê Æ☼«¿♠ ↨ÿÅ⌂ ∟¶Å #ïàì í↨£
C:\PXRWORK>xlate .tbl=Jout %xlate%
The quick brown fox jumps over the lazy dog
------------end screen capture---------------
As such, I had an idea for an Uber-Enigma function that would use a PRNG to randomly create non-authentic rotors and reflectors. Of course while we're going non-authentic I'd allow it to process lower case, numbers and symbols as well like the other ciphers, so each rotor would have more like 85 positions instead of 26. If I implemented this option, instead of guessing which 1 of 2 reflectors and 3 of 5 rotors were used (120 combinations, about 2^7) someone would have to guess the 32-bit key. On top of that ~25-bit security bump, the original 26^6 factor from the unknown starting positions and rings becomes more like 85^6 due to the extended character set, bumping another 10 bits or so. Finally, if you went hog wild and opened up the plugboard to all those other character pairs too.... yikes! I tried to calculate the possible plugboard combinations for 84 characters on an online calculator and it came back ~10^102 by itself. If that's correct, and you add in everything else above the whole machine shakes out around 10^124, or ~412-bits worth. Absolutely insane for a batch file, too bad usability would be down the tubes
Also, there is no reason to limit the number of 'rotors' or translation tables. Just remember, for every 'IN' table (see example above) you've got to have a corresponding 'OUT'.
The processing of numbers using this particular technique is worthy of another thread all by itself.
--
Phil Robyn
Univ. of California, Berkeley
ude tod yelekreb ta nyborp
Re: String Encryption (encoding)
I have two functions called charToNum and numToChar that effectively do a similar thing as your translation tables, and as you said I use them in other contexts. That said, they're hard coded translations, and they only work for Aa through Zz and 1-26, respectively. I don't have the equivalent of a 'symbolToNum' function, or any general purpose way of building translation tables like yours.
I very much like how your functions are table driven, and the tables seem to be easily generated. I'm going to more carefully look through your code and see how the table technique might apply if I ever decide to pursue UberEnigma. Thanks for sharing!
I very much like how your functions are table driven, and the tables seem to be easily generated. I'm going to more carefully look through your code and see how the table technique might apply if I ever decide to pursue UberEnigma. Thanks for sharing!