Retrieving the key from Vigenère Ciphertext
I have been working through how to break a Vigenère cipher. The last time I looked at this, I had established that a key length of 5 characters was used to encrypt this text:
LRPBB HKQBG WIYSA DEURQ LVFOT YEZKV
ELAIG DXADC TRSHB PBBZN TRTWF NSZRH
NXFCC ZVFVB DEXZS ZVABR ZRQTB CEXZG
SEFWF ZYDAB EXAWF TXZCG LRPMR EWMWQ
ASDHU ZWTCY OSGHL ZYDVN YHMBQ DAQOE
NVUSQ LXTCF LRPOE LQUGN ESZQR ZZQFP
ZQQPL PBMAC WISFH XFXWA RXAVV XWQZS
YIHSE ELQZR DWBCE ELAGF EVQHP SIPCH
ELUGU LRPOA OXTSS ZYDTE TIZRF CIBSN
EIPKV ELABR GSUQR ELQTB CQGZN OMOHN
EIPPL OEDHN RRMBN WPRCE ZRQCA PJAFN
WPFVN EWISY WRAKY PXGGR GIDMB YIDSG
TVQHB SMECJ YLAAR DEURQ LVFOT YEZOF
TJTSU LHPCA PRAHU TRSPH EGAAZ LRPOY
WLUGY TJQOA OEFHR YXUCA QSDTE ZQFVV
DQAAR YXISN CIMHS PYPKV ELFVR NEDRV
YEX
I have also previously worked out how to automate the solution of a Caesar shift.
If we know that that the key is 5 letters, then the first, sixth, eleventh (etcetera) letters are all shifted the same way. Similarly, the second, seventh, twelfth are shifted the same.
I can use the method of the Caesar solver to establish what each shift was, and then convert that shift back into the equivalent key letter.
If I do this, I get the key code:
What length key? 5
The best guess of the key is: LEMON
I can then use this key to decrypt the text.
ANDNO WGENT LEMEN SAIDD ARTAG NANWI
THOUT STOPP INGTO EXPLA INHIS CONDU
CTTOP ORTHO SALLF ORONE ONEFO RALLT
HATIS OURMO TTOIS ITNOT ANDYE TSAID
“And now, gentlemen,” said d’Artagnan, without stopping to explain his conduct to Porthos, “All for one, one for all--that is our motto, is it not?”….
The code is posted below.
Links
The title image is from here. Used under CC BY-SA 3.0. Cropped
Recent posts on this topic
More on Cryptography
The Code
# recover the key from a vigenere - you should already have keylength
import numpy as np
from math import sqrt
englishdata=[18374,3418,
4678,11083,
30430,4396,
4571,17068,
13877,223,
1971,10235,
5056,15131,
17579,3353,
245,12919,
14424,21889,
5826,1798,
6737,225,
4305,93]
toteng=0
for i in englishdata:
toteng+=i
keylen=int(input('What length key?'))
print('\n The best guess of the key is: ',end='')
# grab the ciphertext
filetimetable = open("vigciphertext.txt","r")
lines=filetimetable.readlines()
filetimetable.close()
# let's make it all one string
working=""
for line in lines:
for char in line:
asc=ord(char)
if (64 < asc < 91) or ( 96 < asc < 123):
char=char.upper()
working+=char
max=len(working)-1
for i in range(keylen):
done=False
shift=0
lett=i
data=np.full(26,0)
while not done:
data[ord(working[lett])-65]+=1
shift+=keylen
lett=i+shift
if lett >= max:
done=True
# normalising the data
tot=0
for j in data:
tot+=j
if tot>0:
for j in range(26):
data[j]=data[j]*toteng/tot
# now for each batch, find the best caesar shift
error=[]
maxerr=0
for shift in range(26):
err=0
for k in range(26):
err+=sqrt((data[k]-englishdata[(k+shift)%26])**2)
maxerr+=err
error.append(err)
guess=0
for k in range(26):
if error[k]<maxerr:
guess=k
maxerr=error[k]
# and print your guess. As I did the shifting
# back to front. It's not worth fixing that logic
# so I'll fix with simple arithmetic before
# printing
# I also shift the ascii range and turn it
# into a char
print(chr(91-guess),end='')
print()