print "\nMost likely key lengths using Kasiski test:\n%s\n" % (mostLikelyKeysKasiski) # initialize the letter frequency class letFreq = LetterFrequency() # loop through the top 4 most likely key lengths according to the kasiski test for keyLen in mostLikelyKeysKasiski[:2]: # Split the message into keyLen columns msgColumns = polyCi.msgSplit(msg, keyLen) # holds the potential keyword keyword = "" # perform frequency analysis on each column print "Using the Vigenere Square on the top 4 most frequent letters in each column assuming that each corresponds to 'e' results in the following options:" for idx, column in enumerate(msgColumns): # get the most frequent letter in the column columnFrequencies = letFreq.getFrequencies(''.join(column))[1] mostFrequentLetTuple = columnFrequencies[0] mostFreqLet = mostFrequentLetTuple[0] # for tuple in columnFrequencies[:4]: # print vigCi.vigenereSquareDecrypt(tuple[0], 'e') # print "options above" print "Column", idx+1, [vigCi.vigenereSquareDecrypt(tuple[0], 'e') for tuple in columnFrequencies[:6]] # assuming this letter corresponds to 'e', use the vigenere square # to discover the original keyword letter keywordLet = vigCi.vigenereSquareDecrypt(mostFreqLet, 'e') keyword += keywordLet # I discovered this to be the keyword through analyzing the possible combinations across the columns # and put it in manually to make the output more interesting #keyword = 'funny' print "\nPotential keyword: %s" % keyword print "Message deciphered using keyword '%s':\n%s\n" % (keyword, vigCi.decrypt(msg,keyword))
def decrypt(self, msg, mfc=None, mfp=None, verbose=False, showDigraphFrequencies=False, auto=False, bruteAmt=None): # remove spaces from the message msg = ''.join(msg.split()) if verbose: print "Attempting to decipher:\n", msg, "\n" # get the message and default letter frequencies letFreq = LetterFrequency() (freqDict, freqTuples) = letFreq.getFrequencies(msg) (stdDict, stdTuples) = letFreq.getStandardFrequencies() # if the mfc or mfp are None, use the top two most frequent letters # in the ciphertext and english language respectively as defaults mfc = tuple(mfc) if mfc else (freqTuples[0][0], freqTuples[1][0]) mfp = tuple(mfp) if mfp else (stdTuples[0][0], stdTuples[1][0]) if verbose: print "Ciphertext Letter Frequencies:\n%s\n" % freqTuples print "English Language Letter Frequencies:\n%s\n" % stdTuples if showDigraphFrequencies: diFreq = DigraphFrequency() diFreqTuples = diFreq.getFrequencies(msg)[1] diStdTuples = diFreq.getStandardFrequencies()[1] print "Ciphertext Digraph Frequencies:\n%s\n" % diFreqTuples print "English Language Digraph Frequencies:\n%s\n" % diStdTuples print "Using letter in ciphertext: %s" % mfc[0] print "\twith frequency: %.2f%%" % freqDict[mfc[0]] print "Using letter in ciphertext: %s" % mfc[1] print "\twith frequency: %.2f%%\n" % freqDict[mfc[1]] print "Assuming that", mfc[0], "is equivalent to", mfp[0] print "Assuming that", mfc[1], "is equivalent to", mfp[1], "\n" # get the affine key using the mfc letters and mfp letters key = self.getAffineKey(*tuple(map(self.letToInt, mfp+mfc)), verbose=verbose) if auto: iEnd = jEnd = 10 if bruteAmt: iEnd = int(bruteAmt[0]) jEnd = int(bruteAmt[1]) maxIdx = len(freqTuples)-1 for i in range(0, iEnd): for j in range(0, jEnd): if i != j and i <= maxIdx and j <= maxIdx: self.decrypt(msg, mfc=[freqTuples[i][0], freqTuples[j][0]], mfp=mfp, verbose=False, showDigraphFrequencies=showDigraphFrequencies) if key is None: if verbose: print "Unable to decipher using '%s'->'%s' and '%s'->'%s'" % (mfc[0], mfp[0], mfc[1], mfp[1]) return None # Use the key to decrypt plaintext = "" for ch in msg.lower(): cipherInt = ((ord(ch) - 96)*self.multInv[key[0]] - key[1]) % 26 # cover the 'z' case if cipherInt == 0: cipherInt = 26 plaintext += chr(cipherInt + 96) if plaintext and not verbose: print "%s %s %s %s " % (str(key).ljust(8), mfc[0], mfc[1], plaintext) return plaintext