#functions from the Vigenere notebook #Both key and plaintext are strings of lower-case letters #The function returns the ciphertext. def vigencrypt(key,plaintext): keylength=len(key) ciphertext='' for j in range(len(plaintext)): base=ord(plaintext[j])-ord('a') shift=ord(key[j%keylength])-ord('a') ciphertext+=chr(ord('a')+(base+shift)%26) return ciphertext #This computes the inverse of the key: To decrypt, call vigencrypt #with the inverse key. def inverse_key(key): inv='' for ch in key: shift=ord(ch)-ord('a') newshift=(-shift)%26 newch=chr(ord('a')+newshift) inv+=newch return inv #Return the number of characters in the ciphertext that are equal to the character #m places further on, where m is the value of shift. (Here 'shift' means we #are literally moving the text, not shifting in the Caesar cipher sense.) We only #do this for the first len-11 characters of the ciphertext, where #len is the length (so we assume that the key is no more than ten #characters long) def count_coincidences(ciphertext,shift): count = 0 for j in range(len(ciphertext)-11): if ciphertext[j]==ciphertext[j+shift]: count += 1 return count #Return a list of keylength different subtexts #extracted from the ciphertext. The first subtext #consists of characters 0,keylength, 2*keylength, etc.,from the ciphertext #the next of characters 1, 1_keylength, 1+2*keylength, etc. def extract_subtexts(ciphertext,keylength): subtexts=['']*keylength for j in range(len(ciphertext)): subtexts[j%keylength]+=ciphertext[j] return subtexts #Return a score that measures how closely the letter distribution of the text matches that of #English. For idealized English text the score is about 0.0644; for random text about #1/26 = 0.0385 def score(text): freq=[0.0817, 0.0153, 0.0224, 0.0470, 0.121, 0.0217, 0.0210, 0.0606, 0.0724, 0.0010, 0.0090, 0.0379, 0.0315, 0.0684, 0.0773, 0.0170, 0.0009, 0.0575, 0.0605, 0.0885, 0.0283, 0.0092, 0.0260, 0.0013, 0.0226, 0.0002] dist=[0]*26 for ch in text: dist[ord(ch)-ord('a')]+=1 result=0 for j in range(26): result+=dist[j]*freq[j] return result/len(text) #Find the score for all 26 Caesar shifts of the text, and return both the keyletter that #leads to the maximum score, and the maximum score itself. def maxscore(text): maxval=0 maxletter='?' for keyletter in 'abcdefghijklmnopqrstuvwxyz': newscore=score(vigencrypt(inverse_key(keyletter),text)) if newscore>maxval: maxval=newscore maxletter=keyletter return(maxletter,maxval)