kt = KasiskiTest() kasiskiFailed = False repeatedSubstrs = kt.getRepeatedSubstrs(msg, 3) if repeatedSubstrs: print "Repeated substrs of length >= 3:\n%s\n" % repeatedSubstrs print "Potential keys determined by running Kasiski test on each substring:" # print out potential key lengths for every substring for keyLens in kt.getAllPotentialKeyLens(msg, repeatedSubstrs): print keyLens mostLikelyKeysKasiski = kt.getMostLikelyKeyLens(msg, repeatedSubstrs) 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]:
def super_decrypt(self, msg, digraphOverride=None, zeroSystem=False): diFreq = DigraphFrequency() letFreq = LetterFrequency() if digraphOverride is None: (mfd, mfdf) = diFreq.getFrequencies(msg)[1][0] else: # most frequent digraph mfd = digraphOverride mfdIntsTuple = diFreq.digraphToIntTuple(mfd, zeroSystem) mfdConsts = [mfdIntsTuple + (diFreq.letToInt('t', zeroSystem),), mfdIntsTuple + (diFreq.letToInt('h', zeroSystem),)] followingDigraphs = diFreq.getFollowingDigraphs(msg, mfd) followingDigraphConstants = [diFreq.digraphToIntTuple(diTup, zeroSystem) + (diFreq.letToInt('e', zeroSystem),) for diTup in followingDigraphs] # the possible values of a and b in the key matrix: # a b # c d abConsts = mfdConsts[0] abPossibleValues = [self.solveLinEquPair(abConsts, folDiConsts) for folDiConsts in followingDigraphConstants] mostLikelyABValues = self.mostFreqIntPairs(abPossibleValues) bestABValues = [] for abValue, count in mostLikelyABValues: # if the abValue works for all equations, it is very likely # that it is the correct values for a and b if count == len(followingDigraphConstants): bestABValues.append(abValue) # if there are still no abValues # (i.e. no a, b pair works for all of the equations # created by assuming that the digraphs following the mfd correspond to 'e*') # then just pick the top 2 if not bestABValues: bestABValues = mostLikelyABValues[:2] # if there are two a,b pairs to try out, do the following if len(bestABValues) > 1: # of the two most likely values for both a and b, go through every digraph in the message # and determine the int value of a1x+b1y, and a2x + b2y where x and y are the two letters # in the digraph and a1,b1 and a2,b2 are the abValues # convert the resulting int values to letters, and see which key pair produced the more # frequent letter # keep a running tally of the more frequent letters for each pair to see which one is # more likely to be the actual key key1 = bestABValues[0][0] key2 = bestABValues[1][0] msgDigraphs = diFreq.getUniqueDigraphs(msg) key1Lets = [self.diAndKeyToLet(digraph, key1, zeroSystem) for digraph in msgDigraphs] key2Lets = [self.diAndKeyToLet(digraph, key2, zeroSystem) for digraph in msgDigraphs] englishLetterFrequenciesDict = letFreq.getStandardFrequencies()[0] key1MoreFreqCounter = 0 key2MoreFreqCounter = 0 for i in range(0, len(key1Lets)): if englishLetterFrequenciesDict[key1Lets[i]] > englishLetterFrequenciesDict[key2Lets[i]]: key1MoreFreqCounter += 1 else: key2MoreFreqCounter += 1 # try the more likely key first! abKeys = [key1, key2] if key1MoreFreqCounter > key2MoreFreqCounter else [key2, key1] print "Key %s is the more likely of the keys %s and %s" % (abKeys[0], key1, key2) # there is only one AB value, so it must be the correct key pair for a,b!!! else: abKeys = bestABValues # find possible values for c and d that solve the equation cdConsts = mfdConsts[1] cdKeys = self.solveLinEqu(cdConsts) ## print out a description of the progress thus far if digraphOverride is None: print "\nThe most frequent digraph (MFD) in the message was:\n\t%s' with frequency %0.2f%%" % (mfd, mfdf) else: print "\nThe most frequent digraph (MFD) in the message was manually set as:\n\t%s'" % (mfd) print "\nMapping '%s' to 'th' resulted in the following equations:\n\t%da + %db = %d\n\t%dc + %dd = %d" % (mfd, abConsts[0], abConsts[1], abConsts[2], cdConsts[0], cdConsts[1], cdConsts[2]) print "\nLooking at digraphs following '%s' resulted in the following digraphs:\n\t%s" % (mfd, followingDigraphs) print "\nMapping the above digraphs to 'e*' resulted in the following equations:" for fdc in followingDigraphConstants: print "\t%da + %db = %d" % (fdc[0], fdc[1], fdc[2]) print "\nSolving for 'a' and 'b' in these equations resulted in the following most\nlikely key(s):" for abKey in abKeys: print "\ta = %d, b = %d" % (abKey[0], abKey[1]) print "\nSolving for 'c' and 'd' in the original set of equations resulted in the\nfollowing most likely key(s):" for cdKey in cdKeys: print "\tc = %d, d = %d" % (cdKey[0], cdKey[1]) print "\nThese keys as matrices in the form [[a, b], [c, d]] will now be brute forced so \nthat the user can attempt to spot the correct deciphering of the ciphertext.\n\n" for abKey in abKeys: for cdKey in cdKeys: key = [list(abKey), list(cdKey)] print "Trying decryption key: %s, encryption key %s" % (key, self.matrixOps.invertMatrix(key)) print self.decrypt(msg, key, False, zeroSystem)
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