How to resolve the algorithm Vigenère cipher/Cryptanalysis step by step in the Vedit macro language programming language
Published on 12 May 2024 09:40 PM
How to resolve the algorithm Vigenère cipher/Cryptanalysis step by step in the Vedit macro language programming language
Table of Contents
Problem Statement
Given some text you suspect has been encrypted with a Vigenère cipher, extract the key and plaintext. There are several methods for doing this. See the Wikipedia entry for more information. Use the following encrypted text: Letter frequencies for English can be found here. Specifics for this task:
Let's start with the solution:
Step by Step solution about How to resolve the algorithm Vigenère cipher/Cryptanalysis step by step in the Vedit macro language programming language
Source code in the vedit programming language
// (1) Copy text into tmp buffer and remove non-alpha chars.
Chdir(PATH_ONLY)
BOF
Reg_Copy(10, ALL) // copy text to new buffer
Buf_Switch(Buf_Free)
Reg_Ins(10)
BOF
Replace ("|!|A", "", BEGIN+ALL+NOERR) // remove non-alpha chars
Reg_Copy_Block(10,0,EOB_pos) // @10 = text to be analysed
#20 = Buf_Num // buffer for text being analyzed
#21 = Buf_Free // buffer for English frequency list (A-Z)
Buf_Switch(#21)
Ins_Text("8167 1492 2782 4253 12702 2228 2015 6094 6966 153 772 4025 2406 6749 7507 1929 95 5987 6327 9056 2758 978 2360 150 1974 74")
File_Open("unixdict.txt") // or use "|(MACRO_DIR)\scribe\english.vdf"
#23 = Buf_Num // buffer for dictionary
#24 = Buf_Free // buffer for key canditates
Buf_Switch(#24)
for (#1=0; #1<5; #1++) { // Fill table for 5 keys of 50 chars
Ins_Char('.', COUNT, 50)
Ins_Newline
}
#22 = Buf_Free // buffer for results
#25 = Reg_Size(10) // number of letters in the text
#26 = 26 // number of characters in the alphabet
#61 = min(#25/10, 50) // max key length to try
// (2) Check Index of coincidence (or Kp) for each key length
Buf_Switch(#22) // buffer for results
Ins_Text("KeyLen Kp dist ") Ins_Newline
Ins_Text("-----------------") Ins_Newline
#13 = Cur_Pos
#7 = 0 // no Caesar encryption
for (#5=1; #5<=#61; #5++) {
Buf_Switch(#20) // text being analyzed
BOF
#54 = 0; // sum of Kp's
for (#6=0; #6<#5; #6++) { // for each slide
Goto_Pos(#6)
Call("CHARACTER_FREQUENCIES")
Call("INDEX_OF_COINCIDENCE") // #51 = Kp * 10000
#54 += #51
}
#54 /= #5 // average of Kp's
Buf_Switch(#22)
Num_Ins(#5, COUNT, 3) // write key length
IT(": ")
Num_Ins(#54, NOCR) // average Kp
Num_Ins(670-#54) // distance to English Kp
}
Buf_Switch(#22)
Sort_Merge("5,12", #13, Cur_Pos, REVERSE) // sort the results by Kp value
Ins_Newline
// (3) Check the best 4 key lengths to find which one gives the best decrypt result
#38 = 0 // max number of correct characters found
#19 = 1 // best key length
for (#14 = 0; #14<4; #14++) { // try 4 best key lengths
Buf_Switch(#22) // results buffer
Goto_Pos(#13) Line(#14)
#5 = Num_Eval(SUPPRESS) // #5 = key length
Call("FIND_KEYS") // find Caesar key for each key character
#4 = -1 // try best match key chars only
Call("BUILD_KEY")
EOF
Ins_Text("Key length ")
Num_Ins(#5, LEFT)
Reg_Ins(10) // encrypted text
BOL
Call("DECRYPT_LINE")
BOL
Call("FIND_ENGLISH_WORDS") // #37 = number of English chars
EOL Ins_Newline
Ins_Text("Correct chars: ")
Num_Ins(#37)
if (#37 > #38) {
#38 = #37
#19 = #5
}
Update()
}
Ins_Text("Using key length: ") Num_Ins(#19) Ins_Newline
#5 = #19
Call("FIND_KEYS") // find Caesar key for each key character
// (4) Decrypt with different key combinations and try to find English words.
// Try key combinations where max one char is taken from 2nd best Caesar key.
#38 = 0 // max number of chars in English words found
#39 = -1 // best key number found
for (#4 = -1; #4 < #19; #4++)
{
Call("BUILD_KEY")
Buf_Switch(#22) // results
Reg_Ins(10) // encrypted text
BOL
Call("DECRYPT_LINE")
BOL
Update()
Call("FIND_ENGLISH_WORDS") // #37 := number of correct letters in text
if (#37 > #38) {
#38 = #37 // new highest number of correct chars
#39 = #4 // new best key
}
EOL IT(" -- ") // display results
Num_Ins(#4, COUNT, 3) // key number
Ins_Text(": ")
for (#6=0; #6<#19; #6++) { // display key
#9 = 130 + #6
Ins_Char(#@9)
}
Ins_Text(" correct chars =")
Num_Ins(#37)
}
Ins_Text("Best key = ")
Num_Ins(#39, LEFT)
#4 = #39
Ins_Newline
// Display results
//
Buf_Switch(#24) // table for key canditates
BOF
Reg_Copy_Block(14, Cur_Pos, Cur_Pos+#19) // best Caesar key chars
Line(1)
Reg_Copy_Block(15, Cur_Pos, Cur_Pos+#19) // 2nd best Caesar key chars
Call("BUILD_KEY")
Buf_Switch(#22)
Ins_Text("Key 1: ") Reg_Ins(14) Ins_Newline
Ins_Text("Key 2: ") Reg_Ins(15) Ins_Newline
Ins_Text("Key: ")
for (#6=0; #6 < #19; #6++) {
#9 = #6+130
Ins_Char(#@9)
}
Ins_Newline
Ins_Newline
// decrypt the text with selected key
Ins_Text("Decrypted text:") Ins_Newline
Reg_Ins(10)
BOL
Call("DECRYPT_LINE")
BOL Reg_Copy(13,1)
EOL Ins_Newline
// Find English words from the text
Reg_Ins(13)
Call("FIND_ENGLISH_WORDS")
EOL
Ins_Newline
Num_Ins(#37, NOCR) IT(" of ")
Num_Ins(#25, NOCR) IT(" characters are English words. ")
Ins_Newline
Buf_Switch(#20) Buf_Quit(OK)
Buf_Switch(#21) Buf_Quit(OK)
Buf_Switch(#23) Buf_Quit(OK)
Buf_Switch(#24) Buf_Quit(OK)
Statline_Message("Done!")
Return
/////////////////////////////////////////////////////////////////////////////
//
// Caesar decrypt current line and count character frequencies.
// in: #5 = step size, #7 = encryption key, #26 = num of chars in alphabet
// out: #65...#90 = frequencies, #60 = number of chars
:CHARACTER_FREQUENCIES:
Save_Pos
for (#8 = 'A'; #8<='Z'; #8++) {
#@8 = 0 // reset frequency counters
}
#60 = 0 // total number of chars
while (!At_EOL) {
if (Cur_Char >= 'A' && Cur_Char <= 'Z') {
#8 = (Cur_Char-'A'+#26-#7) % #26 + 'A' // decrypted char
#@8++
#60++
}
Char(#5)
}
Restore_Pos
Return
// Calculate Index of Coincidence (Kp).
// in: character frequencies in #65...#90, #60 = num of chars
// out: #51 = IC * 10000
//
:INDEX_OF_COINCIDENCE:
Num_Push(10,15)
#10 = 0
for (#11 = 'A'; #11<='Z'; #11++) {
#10 += (#@11 * (#@11-1)) // Calculate sigma{ni * (ni-1)}
}
#12 = #60 * (#60-1) // #12 = N * (N-1)
#51 = #10 * 10000 / #12 // #51 = Kp * 10000
Num_Pop(10,15)
Return
// Find best and 2nd best Caesar key for each character position of Vigenère key.
// in: #5=step size (key length)
// out: keys in buffer #24
//
:FIND_KEYS:
for (#6 = 0; #6 < #5; #6++) { // for each char position in the key
#30 = -1 // best key char found so far
#31 = -1 // 2nd best key char
#32 = MAXNUM // smallest error found so far
#33 = MAXNUM // 2nd smallest error found so far
for (#7 = 0; #7 < #26; #7++) { // for each possible key value
#35 = 0 // total frequency error compared to English
Buf_Switch(#20) // text being analyzed
Goto_Pos(#6)
Call("CHARACTER_FREQUENCIES")
Buf_Switch(#21) // English frequency table
BOF
for (#8 = 'A'; #8<='Z'; #8++) { // calculate total frequency error
#34 = Num_Eval(SUPPRESS+ADVANCE)
#35 += abs((#@8*100000+50000)/#60-#34)
}
if (#35 < #32) { // found better match?
#33 = #32
#32 = #35
#31 = #30
#30 = #7
} else {
if (#35 < #33) { // 2nd best match?
#33 = #35
#31 = #7
}
}
}
Buf_Switch(#24) // table for key canditates
BOF
Goto_Col(#6+1)
Ins_Char(#30+'A', OVERWRITE) // save the best match
Line(1)
Goto_Col(#6+1)
Ins_Char(#31+'A', OVERWRITE) // save 2nd best match
}
Buf_Switch(#22) // results buffer
Return
// Combine actual key from 1st and 2nd best Caesar key characters
// Use 1st key chars and (possibly) one character from 2nd key.
// #4 = index of the char to be picked from 2nd key, -1 = none.
// #5 = key length
//
:BUILD_KEY:
Buf_Switch(#24) // table for key canditates
BOF
for (#6=0; #6<#5; #6++) { // copy 1st key
#8 = 130 + #6
#@8 = Cur_Char
Char(1)
}
if (#4 >= 0) {
#8 = 130 + #4 // pick one char from 2st key
Line(1)
Goto_Col(#4+1)
#@8 = Cur_Char
}
Buf_Switch(#22) // results buffer
Return
// Decrypt text on current line
// in: #5 = key length, #130...#189 = key
//
:DECRYPT_LINE:
Num_Push(6,9)
#6 = 0
While (!At_EOL) {
#9 = #6+130
#7 = #@9
#8 = (Cur_Char - #7 + #26) % #26 + 'A' // decrypted char
Ins_Char(#8, OVERWRITE)
#6++
if (#6 >= #5) {
#6 = 0
}
}
Num_Pop(6,9)
Return
// Find English words from text on current line
// out: #37 = number of chars matched
//
:FIND_ENGLISH_WORDS:
Buf_Switch(#23) // dictionary
BOF
While (!At_EOF) {
Reg_Copy_Block(12, Cur_Pos, EOL_Pos)
if (Reg_Size(12) > 2) {
Buf_Switch(#22) // buffer for results
BOL
while (Search_Block(@12, Cur_Pos, EOL_Pos, NOERR)) {
Reg_Ins(12, OVERWRITE)
}
Buf_Switch(#23)
}
Line(1, ERRBREAK)
}
Buf_Switch(#22)
BOL
#37 = Search_Block("|V", Cur_Pos, EOL_Pos, ALL+NOERR)
Return
You may also check:How to resolve the algorithm Knight's tour step by step in the Fortran programming language
You may also check:How to resolve the algorithm Probabilistic choice step by step in the Erlang programming language
You may also check:How to resolve the algorithm Abundant odd numbers step by step in the Kotlin programming language
You may also check:How to resolve the algorithm A+B step by step in the Phixmonti programming language
You may also check:How to resolve the algorithm Stair-climbing puzzle step by step in the OCaml programming language