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]:
Exemple #2
0
 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