Decryption by key substitution - Python

Viewed 152

[After writing this I've realised that I'm substituting the already partially decrypted message, so I seem to have found the error but now I can't seem to figure it out how to replace the letters one by one without using a bunch of if-else statements which seem very inefficient. Any suggestions?]

Part of the encrypted message

THEXMUNITIONSXFACTORYXREPORTEDXDAMAGEDXINXBOMBINGXRAIDXSTOPXKEEPXLOWXPROFILEXSTOPXURGENTXMESSAGESXCOMPROMISEDXSWITCHXTOXENCRYPTEDXURGENTXSTOPASJHXBLDZBLVZJXWZBHDFFVMYHSJYDMBJDOSUBGJDKBFSPWBHDMJSHJBAYJXBSRWMJBDZSMRWBGJDKBSNDYOBJXWBDJXWZBHDMJSHJBSJBSIIBHDGJGBGJDKBSHJYNSJWBDKWZSJYDMBEIVWBGKDDMBGJDKBZWKDZJBDMBJXWBKDIYHWBGJSJYDMBGJDKBSHJYNSJWBDKWZSJYDMBMDZJXADDOGBGJDKBZWKDZJBDMBUDVZBGJSJVGBGJDKBSBESJJIWGXYKBGWWMBOWKSZJYMRBLZDFBOWWKBXSZEDVZBGJDKBMWBJBFWWJYMRBSJBCICCBTVIVBJDMYRXJBJXWBKSZPBVMOWZBJXWBDSPBGJDKBPWWKBIDABKZDLYIWBGJDKBOYGZWRSZOBISGJBFWGGSRWBGJDKBYMNWGJYRSJWBZVFDVZGBDLBGJZYPWGBGJDKBPWWKBIDABKZDLYIWBGJDKBVMPMDAMBNWGGWIBYGBODHPWOBSJBOWWKBXSZEDVZBGJDKBJXWBODRBJZSYMWZBFSUBEWBODVEIWBSRWMJBWBWZHYGWBHSVJYDMBGJDKBDKWZSJYDMBMDZ

I need to decrypt a message, by key substitution. I'm given an ordered string by how frequently each letter appears in previously unencrypted messages, and then I'm told that to decrypt it all that needs to be done is to substitute the most frequent letter in the encrypted text by the most frequent in past non-encrypted texts and so on.

I'm also told to ignore the first 141 characters, since these have already been decrypted.

As you can see from the code above, I did the following:

  1. Copy the encrypted string from the 142th character onwards, to deal only with the bit that is still encrypted

  2. Create a dictionary with the letters and the number of times they occur in the message

  3. Convert that dictionary into a list, and sorting it into descending order

  4. Finally, I iterate through the "frequency string" (which is essentially the key that I was given indicating how frequently the letters in unencrypted messages appear) and match it to the letters in the message in the ordered by frequency list. And use the replace() string method replace them

encrypted = message[142:] frequencyString = "XTOEASRINPDCLHUMGBFYWVKZJQ" print(orderedChars) #for debuging purposes

def counter(chars):
    d = {}
    for char in message:
        d[char] = d.get(char,0) + 1
    return d

charCount = counter(message)

def sortChars(charCount):
    orderedChars = []
    for k in charCount:
        orderedChars.append([charCount[k],k])
    orderedChars = sorted(orderedChars, reverse=True)
    return orderedChars

orderedChars = sortChars(charCount)

def charMatch(orderedChars, frequencyString):
    global encrypted
    i=-1
    for char in frequencyString:
        i+=1
        print(orderedChars[i][1]+"->"+char,end=" $ ") #for debuging purposes
        encrypted = encrypted.replace(orderedChars[i][1],char)
    encrypted = encrypted.replace("X"," ")
    print()
    print()#for debuging purposes
    print(encrypted) #for debuging purposes
    return encrypted
print()
charMatch(orderedChars,frequencyString)

OUTPUT

[[1449, 'B'], [830, 'J'], [784, 'D'], [748, 'W'], [578, 'S'], [530, 'G'], [469, 'Z'], [413, 'Y'], [403, 'M'], [343, 'K'], [251, 'O'], [233, 'X'], [232, 'H'], [229, 'I'], [191, 'V'], [190, 'F'], [145, 'E'], [142, 'R'], [111, 'L'], [95, 'U'], [86, 'A'], [77, 'N'], [70, 'P'], [22, 'T'], [11, 'C'], [6, 'Q']]

B->X $ J->T $ D->O $ W->E $ S->A $ G->S $ Z->R $ Y->I $ M->N $ K->P $ O->D $ X->C $ H->L $ I->H $ V->U $ F->M $ E->G $ R->B $ L->F $ U->Y $ A->W $ N->V $ P->K $ T->Z $ C->J $ Q->Q $

WZFJJFDBJFYBZJGBJFDMMYVHFWZHDVJZDDWYJSZDKJMWKGJFDVZWFZJWHZJJWBGVZJDBWVBGJSZDKJWVDHDJZJGJDZJGBJFDVZWFZJWZJWHHJFDSZSJSZDKJWFZHVWZGJDKGBWZHDVJGHYGJSKDDVJSZDKJBGKDBZJDVJZJGJKDHHFGJSZWZHDVJSZDKJWFZHVWZGJDKGBWZHDVJVDBZJWDDDSJSZDKJBGKDBZJDVJYDYBJSZWZYSJSZDKJWJGWZZHGSJHKJSGGVJDGKWBZHVBJFBDMJDGGKJJWBGDYBJSZDKJVGJZJMGGZHVBJWZJJHJJJZYHYJZDVHBJZJZJGJKWBKJYVDGBJZJGJDWKJSZDKJKGGKJHDWJKBDFHHGJSZDKJDHSBGBWBDJHWSZJMGSSWBGJSZDKJHVVGSZHBWZGJBYMDYBSJDFJSZBHKGSJSZDKJKGGKJHDWJKBDFHHGJSZDKJYVKVDWVJVGSSGHJHSJDDFKGDJWZJDGGKJJWBGDYBJSZDKJZJGJDDBJZBWHVGBJMWYJGGJDDYGHGJWBGVZJGJGBFHSGJFWYZHDVJSZDKJDKGBWZHDVJVDBZJWDDDSJJWSJGGGVJSYFFGSSFYHJSZDKJVGJZJMGGZHVBJWZJKBJJJZYHYJZDMDBBDWJWZJZJGJDHDJHHDVJSZDKJDDJVDZJMWKGJFDVZWFZJYVZHHJZJGJYSYWHJDWYJSZDKJDDJVDZJMWKGJ

1 Answers

You have most of the code in place to find the char frequencies. You mentioned that you figured out doing an in-place substitution doesn't work. So you need to create a separate buffer to store the decrypted message. I found it easier to create a map of the 2 frequency strings:

# Your other code....
# orderedChars needs to be converted to string like frequencyString...
# Zip the 2 frequency strings to a dictionary
letterMap = dict(zip(orderedChars, frequencyString))
# Create an empty array to hold decrypted msg
decrypted = [''] * len(encrypted)
# Decrypt the message letter by letter
for i,c in enumerate(encrypted):
    decrypted[i] = letterMap[c]
# Convert to string and print   
print(''.join(decrypted).replace('X', ' '))

As mentioned in the comments, you can also do this with translate.

# Build the translation table
translationTable = str.maketrans(orderedChars, frequencyString)
# Decrypt and print
print(encrypted.translate(translationTable).replace('X', ' '))

Both codes produce the same output.

Related