2

I've been having trouble figuring out how to code and implement a couple functions in my program. The encrypt method that calls the get_col_index and get_row_index methods, and also the get_col_index and get_row_index methods. The encrypt method is called by the main method, and the message, key, and vignere_square is passed through. The Vignere Matrix cipher is already generated in a separate function. The encrypt method then calls the get_col_index and get_row_index methods to encrypt the message, skipping over everything that isn't a letter.

A big part of my problem has been figuring out how to encode the message using the get_row_index and get_col_index methods. for get_col_index, we pass through the message and vigenere square, and it returns the column index of each letter in the message. For the get_row_index, we pass through the key and vigenere square, and it returns the row index for each letter of the key.. I've been trying to use for loops for these functions, and I usually get back the message(not encrypted), a list with random letters, or errors. Here is my latest attempt, I tried using an append in the encrypt method but it just adds the message and key into a list.

def main():
    vig_square = create_vig_square()
    message = input("Enter a multi-word message with punctuation: ")
    input_key = input("Enter a single word key with no punctuation: ")
    msg = message.lower()
    key = input_key.lower()
    print("The encoded message is: ",encrypt(msg, key, vig_square))
    print("The decoded message is: ")

def encrypt(msg, key, vig_square):
    msg_char = msg
    key_char = key
    coded_msg = list()
    coded_msg.append(get_col_index(msg_char, vig_square))
    coded_msg.append(get_row_index(key_char, vig_square))
    return coded_msg

def get_col_index(msg_char, vig_square):
    column_index = ""
    for i in range(len(vig_square)):
        column_index = msg_char
        i += 1
    return column_index

def get_row_index(key_char, vig_square):
    row_index =  ""
    for i in range(len(vig_square)):
        row_index = key_char
        i += 1
    return row_index


def create_vig_square():
    vig_square = list()
    for row in range(26):
        next_row = list()
        chr_code = ord('a') + row
        for col in range(26):
            letter = chr(chr_code)
            next_row.append(letter)
            chr_code = chr_code + 1
            if chr_code > 122:
                chr_code = ord('a')
        vig_square.append(next_row)
    return vig_square

main()

I expect output to be like this:

The eagle has landed.

Enter a single word key with no punctuation:
LINKED

The encoded message is:
epr oejwm ukw olvqoh.

The decoded message is:
the eagle has landed.
Chey
  • 35
  • 4

1 Answers1

2

The vignere cipher has some nice properties, given that the vignere square is cyclical, both horizontally and vertically, which means that the lookup can be done as so:

# assume col and row are lowercase letters
def vignere_lookup(col, row):
  base = ord('a')
  index = ((ord(col) - base) + (ord(row) - base)) % 26
  return chr(base + index)

This assigns the letters a-z to the numbers 0-25, adds the columns and row letter, and uses modulo to go around in case the value is higher than 'z'(25)

This means encryption can be done with:

def encrypt(message, key):
  ciphertext = ''
  key_index = 0
  # convert to lower case
  for letter in message.lower():
    # only encrypt the letters a-z, for space, punctuation, numbers, just add the raw letter
    if ord(letter) in range(ord('a'), ord('z') + 1):
      # make sure to lowercase the key as well
      ciphertext += vignere_lookup(letter, key[key_index % len(key)].lower())
      key_index += 1
    else:
      ciphertext += letter

  return ciphertext

Similarly, the decryption lookup can be done using subtraction, taking advantage of the fact that in python, using modulo on negative numbers cycle as we would hope. This gives the decryption as:

def vignere_reverse(col, target):
  base = ord('a')
  # notice the subtraction instead of addition
  index = ((ord(col) - base) - (ord(target) - base)) % 26
  return chr(base + index)

def decrypt(cipher, key):
  message = ''
  key_index = 0
  for letter in cipher.lower():
    if ord(letter) in range(ord('a'), ord('z') + 1):
      message += vignere_reverse(letter, key[key_index % len(key)].lower())
      key_index += 1
    else:
      message += letter

  return message

Put all of this together and you can do:

cipher = encrypt("The eagle has landed.", "LINKED")
print(cipher) # prints "epr oejwm ukw olvqoh."
decrypted = decrypt(cipher, "LINKED")
print(decrypted) # prints "the eagle has landed."
Ezphares
  • 359
  • 1
  • 7
  • Thank you for your response. How would I implement this method into two separate functions (get_row_index and get_col_index)? The col function gets the column number from the message, and the row function gets the row index from the key, then combines them in the encrypt function to create the cipher. I've tried taking snippets of your code and implementing them into my functions, but it doesn't come out right. – Chey Sep 02 '19 at 21:21
  • That depends what exactly you mean by index. If it is indices for looking into a 2 dimensional array, as you store the vignere square, the index of a (lowercase) letter is simply `ord(letter) - ord('a')`. Note that you don't actually have to generate the square to encrypt and decrypt. – Ezphares Sep 02 '19 at 21:32