5

After watching this tutorial about the Vigenere Cipher, I (hopefully) understand its basic concepts. We want to assign a key to a string, and then shift each letter in the string by the (0-based) alphabet-position value of each letter in the key. So when using bacon as the key,

Meet me in the park at eleven am
baco nb ac onb acon ba conbac on

becomes

Negh zf av huf pcfx bt gzrwep oz

As I'm writing a Vigenere Cipher from scratch, I only know that the first step is to assign the key to a string. And while I'm doing this, I want to recognize whether or not each of the characters is alpha so that I can preserve any special characters in the string (!, @, #, etc.) if there are any.

text = input("Enter some text:")

def encrypt(text):

#key = bacon
encrypted = []
baconvalue = {'A':0, 'a':0, 'B':1, 'b':1, 'C':2, 'c':2, 'D':3, 'd':3, 'E':4, 'e':4, 'F':5, 'f':5, 'G':6, 'g':6, 'H':7, 'h':7, 'I':8, 'i':8, 'J':9, 'j':9, 'K':10, 'k':10, 'L':11, 'l':11, 'M':12, 'm':12, 'N': 13, 'n':13, 'O':14, 'o':14, 'P':15, 'p':15, 'Q':16, 'q':16, 'R':17, 'r':17, 'S':18, 's':18, 'T':19, 't':19, 'U':20, 'u':20, 'V':21, 'v':21, 'W':22, 'w':22, 'X':23, 'x':23, 'Y':24, 'y':24, 'Z':25, 'z':25 }


for letter in text:

#assign 'bacon' to text to get rotation value for each character
#preserve alpha characters

        if letter.isalpha():  
      
#character in string rotates x amount according to the corresponding value of char in bacon
        
            encrypted.append(letter, baconvalue)        

        else:    
    
            encrypted.append(letter)            
    
        return ''.join(encrypted)

print(encrypt(text,))

But as you can see, I don't know where to start as far as how to assign bacon to the string. Am I at least on the right track?

martineau
  • 119,623
  • 25
  • 170
  • 301
SpeakInCode43
  • 580
  • 4
  • 11
  • 23
  • 3
    The checking for alpha is a red herring and will just confuse you and your implementation. Use modulo or xor. There's a good set of exercises here that take you through basic substitution ciphers with Set 1 and a lot more if you keep going - http://cryptopals.com – pvg Dec 10 '16 at 21:15

4 Answers4

3

The way to index over your string with the value that the user enters as the key, is to create a variable starting_index and set it to = 0. This way, iteration will start at the first character in the string and you'll be able to generate a rotation value using the alphabet_pos dictionary that you created earlier.

Use your rotate function to rotate the letters by the new rotation variable that you created. If the letter is found in your dictionary, the encrypt function will append this new letter and continue until you've reached the last index value in the key. Then it will join together the encrypted letters.

alphabet_pos = {'A':0, 'a':0, 'B':1, 'b':1, 'C':2, 'c':2, 'D':3, 'd':3,
'E':4, 'e':4, 'F':5, 'f':5, 'G':6, 'g':6, 'H':7, 'h':7, 'I':8, 'i':8,
'J':9, 'j':9, 'K':10, 'k':10, 'L':11, 'l':11, 'M':12, 'm':12, 'N': 13,
'n':13, 'O':14, 'o':14, 'P':15, 'p':15, 'Q':16, 'q':16, 'R':17, 'r':17,
'S':18, 's':18, 'T':19, 't':19, 'U':20, 'u':20, 'V':21, 'v':21, 'W':22,
'w':22, 'X':23, 'x':23, 'Y':24, 'y':24, 'Z':25, 'z':25 }

def alphabet_position(letter):
    alphabet_pos = {'A':0, 'a':0, 'B':1, 'b':1, 'C':2, 'c':2, 'D':3,
'd':3, 'E':4, 'e':4, 'F':5, 'f':5, 'G':6, 'g':6, 'H':7, 'h':7, 'I':8,
'i':8, 'J':9, 'j':9, 'K':10, 'k':10, 'L':11, 'l':11, 'M':12, 'm':12,
'N': 13, 'n':13, 'O':14, 'o':14, 'P':15, 'p':15, 'Q':16, 'q':16,
'R':17, 'r':17, 'S':18, 's':18, 'T':19, 't':19, 'U':20, 'u':20, 'V':21,
'v':21, 'W':22, 'w':22, 'X':23, 'x':23, 'Y':24, 'y':24, 'Z':25, 'z':25
}
    pos = alphabet_pos[letter]
    return pos

def rotate(letter, rot):
    shift = 97 if letter.islower() else 65
    return chr((ord(letter) + rot - shift) % 26 + shift)

def encrypt(text, key):
    encrypted = []    
    starting_index = 0
    for letter in text:
    # if it's alphanumerical, keep it that way
    # find alphabet position
        rotation = alphabet_position(key[starting_index])
    # if it's a space or non-alphabetical character, append and move on
        if not letter in alphabet_pos:
            encrypted.append(letter)
        elif letter.isalpha():            
            encrypted.append(rotate(letter, rotation))             

    #if we've reached last index, reset to zero, otherwise + by 1
        if starting_index == (len(key) - 1): 
            starting_index = 0
        else: 
            starting_index += 1

    return ''.join(encrypted)    

text = input("Enter some text:")
key = input("Enter a key:")

print(encrypt(text,key))
SpeakInCode43
  • 580
  • 4
  • 11
  • 23
  • I believe this would iterate the key even on non-alphabetical characters, which may not be desirable. You could prevent this just by adding a continue after the letter append for non-alphabetical characters, though. – erosebe Dec 13 '18 at 17:50
1

Possibly my full implementation for deciphering a Vigenère cipher might help you and others (it uses the Friedman test method with auto-correlation) understanding the method.

You can find the code here: https://github.com/ferreirafabio/vigenere-py

whiletrue
  • 10,500
  • 6
  • 27
  • 47
1

So my code is quite long but works quite well. Hope it helps

def VigenereCiphre(line, key):
    isupper = True
    key = key.strip(' ')
    key = key.upper()
    k = list(key)
    print(k)
    returnLine = []
    i = 0
    for char in list(line):

        # Check if current character is symbol. If true append it to encrypted line
        if ord(char) < 65 or ord(char) > 122:
            returnLine.append(char)
        elif ord(char) > 90 and ord(char) < 97:
            returnLine.append(char)
        else:
            # Checks if letter is capital. If it's not, stores isupper to false and make it uppercase
            if ord(char) > 90:
                isupper = False
                char = char.upper()

            else:
                isupper = True

            # Checks if key's index isn't out of range, if it is, set it back to 0
            print(isupper)
            if i == len(k):
                i = 0

            # create new character based on it's value

            c = ord(char) + ord(k[i]) - 65
            if c > 90:
                c = c - 25

            if isupper == False:
                print(c)
                c = c + 32
                print(c)

            returnLine.append(chr(c))

            i += 1

    a = ''
    return a.join(returnLine)
Jan Zajc
  • 37
  • 6
0
# Without import libraries
# Keep it simple

alph = 'abcdefghijklmnopqrstuvwxyz'

def encrypt_letter(char, key):
    if char.isalpha():
        shift = alph.index(char) 
        shifted_alph = alph[shift:] + alph[:shift] # rotate
        result = shifted_alph[key]
    else:
        result = char
    return result

def calculate_shifts(letter):
    return alph.index(letter)

def encrypt_text(txt, keyword):
    txt = txt.lower()
    en_ls = list()

# # >>> Method 1 for padding
#     keyword_times_in_txt = (int(len(txt) / len(keyword))+1) # round up
#     txt_padding_with_keyword = (keyword * keyword_times_in_txt)
#     txt_padding_with_keyword = txt_padding_with_keyword[:len(txt)] # slice at length of txt
# # <<< Method 1 for padding

# >>> Method 2 for padding
    txt_padding_with_keyword = list()
    for i in range(len(txt)):
        len_keyword = len(keyword)
        txt_padding_with_keyword.append(keyword[i % len_keyword])
# <<< Method 2 for padding    
    
    for i in range(len(txt)):
        key = calculate_shifts(txt_padding_with_keyword[i])
        char = txt[i]
        en_letter = encrypt_letter(char, key)
        en_ls.append(en_letter)
        
    result = "".join(en_ls)
    
    print(f"Keyword                 : {keyword}")
    print(f"Text                    : {txt}")
    print(f"Padded text with keyword: {''.join(txt_padding_with_keyword)}")

    return result 

# in_txt = input("Which text should be encrypted?")
# in_keyword = input("Which keyword should be used?")

in_txt = "Python is Really Beautiful!"
in_keyword  = "Random"

in_txt = in_txt.lower()
in_keyword = in_keyword.lower()

print(f"Encrypted text          : {encrypt_text(in_txt, in_keyword)}")

# Output:
# Keyword                 : random
# Text                    : python is really beautiful!
# Padded text with keyword: randomrandomrandomrandomran
# Encrypted text          : gygkcz if fqrlyb nvahwwrll!
itsergiu
  • 662
  • 6
  • 11