Vigenere cipher working as intended, but only after removing spaces

46 views Asked by At

My vigenere cipher only gives me a coherent sentence when i remove all the spaces in the message and keyL. if i add them back and try to write my own code to account for it i get gibberish. im 90% sure this is due to me making an error when im trying to align the keyL to message so that it is the same length as message and has the same spaces in the same spots. can someone help me understand what i need to add in that will properly account for the spaces without making whats currently actually working properly dysfunctional?

message = 'txm srom vkda gl lzlgzr qpdb? fepb ejac! ubr imn tapludwy mhfbz cza ruxzal wg zztcgcexxch!'.replace(' ', '')

key = 'friends'

def decryption(message, key):
    alpha = 'abcdefghijklmnopqrstuvwxyz'
    punc = '.,/?!@#%^&*":'


 
    keyL = ''.replace(' ', '')
    for i in range(len(message)):
        if message[i] == ' ':
            keyL += ' '
        else:
            keyL += key[i % len(key)]

    
    letter_to_index = dict(zip(alpha, range(len(alpha))))
    index_to_letter = dict(zip(range(len(alpha)), alpha))

    
    decrypted = ''
    i = 0
    for char in message:
        if char in punc:
            decrypted += char
        else:
            number = ((letter_to_index[char]) + (letter_to_index[keyL[i]])) % len(alpha)
            decrypted += (index_to_letter[number])
            i += 1

    return decrypted

print(decryption(message, key))

OUTPUT: youwereabletodecodethis?nicework!youarebecomingquitetheexpertatcrytography!

EXPECTEDOUTPUT: you were able to decode this? nice work! you are becoming quite the expert at cryptography!

1

There are 1 answers

0
larsks On

This loop is generating an invalid value for keyL:

    keyL = ''.replace(' ', '')
    for i in range(len(message)):
        if message[i] == ' ':
            keyL += ' '
        else:
            keyL += key[i % len(key)]

Print out the value of keyL after this loop and you will see:

fri ndsf iend fr endsfr endsf iend frien sfr end friendsf iends rie dsfrie ds riendsfriend

Look carefully and you'll see that there are missing letters. Instead of friendsfriendsfriends you have frindsfiendfrends. This is happening because you're incrementing i for every loop iteration, but only extending keyL when the character is not a space.

When you use this string to decrypt the message, you get invalid results.

There are several ways of fixing this up. Your existing code is doing a lot of extra work; you could reduce it to something like:

import string
from itertools import cycle

message = "txm srom vkda gl lzlgzr qpdb? fepb ejac! ubr imn tapludwy mhfbz cza ruxzal wg zztcgcexxch!"
key = "friends"

letter_to_index = dict(zip(string.ascii_lowercase, range(len(string.ascii_lowercase))))


def decryption(message, key):
    keyL = cycle(key)
    decrypted = ""

    for char in message.lower():
        if char in string.ascii_lowercase:
            decrypted += string.ascii_lowercase[
                (letter_to_index[char] + letter_to_index[next(keyL)])
                % len(string.ascii_lowercase)
            ]
        else:
            decrypted += char

    return decrypted


print(decryption(message, key))

The only real magic here is the use of itertools.cycle, which gives us an iterator that for each call to next() will return the next character in key, restarting back at the beginning when we reach the end.

Note also that we don't need index_to_letter, because we can access a string with a numeric index. If we have a string variable like ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz', then ascii_lowercase[0] is a, and so forth.


Using something closer to your original logic, we could do this instead:

def decryption(message, key):
    keyL = ""
    message = message.lower()
    decrypted = ""

    i = 0
    for char in message:
        if char != " ":
            keyL += key[i % len(key)]
            i += 1

    i = 0
    for char in message:
        if char in string.ascii_lowercase:
            decrypted += string.ascii_lowercase[
                (letter_to_index[char] + letter_to_index[keyL[i % len(keyL)]])
                % len(string.ascii_lowercase)
            ]
            i += 1
        else:
            decrypted += char

    return decrypted