Vigenere Cipher Key not working as expected - python

I have constructed a Vigenere cipher function that works reasonably well, except for the fact that it does not pass the coding test that I'm required to run it through.
This is due to the fact that I'm using the ordinal values, while the test is expecting me to use a function to just rotate through a string letters, I think.
I have already run through the issue of dealing with non-alpha characters and got it to work with both upper case and lower case characters. However, it seems to fall apart if the key has any variance of upper case or lower case characters i.e. the key is lower case but the plain text is upper.
def encrypt(text, key):
cipher_text = []
key = list(key)
if len(text) == len(key):
return(key)
else:
for i in range(len(text) -
len(key)):
key.append(key[i % len(key)])
for i in range(len(text)):
a = text[i]
if a.isalpha() and a.islower():
x = ((ord(text[i]) + ord(key[i])-97) % 26 + 97)
cipher_text.append(chr(x))
elif a.isalpha() and a.isupper():
x = ((ord(text[i]) + ord(key[i])-65) % 26 + 65)
cipher_text.append(chr(x))
else:
cipher_text.append(a)
return("" . join(cipher_text))
def main():
mess = input(str("What is message?"))
key = input("What is key?")
print(encrypt(mess, key))
if __name__ == '__main__':
main()
For vigenere.encrypt('BaRFoo', 'BaZ')
You should have returned this:
'CaQGon'
But you actually returned this:
'PtDTha'

from your single example it seems like the key is case insensitive:
def encrypt_letter(c, k):
if c.isupper():
base = ord('A')
else:
base = ord('a')
return chr((ord(c) - base + ord(k) - ord('a')) % 26 + base)
def encrypt(text, key):
key = key.lower()
return "".join([encrypt_letter(text[i], key[i % len(key)]) for i in range(len(text))])
def main():
print(encrypt('BaRFoo', 'BaZ'))
if __name__ == '__main__':
main()

First of all, I don't program in python, so excuse my form. I however tested everything in online compiler.
Before I answer your question, I'm not sure about this segment:
if len(text) == len(key):
return(key)
else:
for i in range(len(text) -
len(key)):
key.append(key[i % len(key)])
I read that as "If the key has the same length as plaintext, the key is the ciphertext", which obviously isn't true, unless the key is "aaaaaaaaa...". I would expect something like this:
if len(text) > len(key):
for i in range(len(text) -
len(key)):
key.append(key[i % len(key)])
# else:
# I don't care - the key is long enough
From Kfir Dadosh's answer I'd also point out, that you don't actually need this step and might as well directly access the key as key[i % len(key)].
As for the issue you were referring to. You only check whether plaintext (message) is lowercase or uppercase and change the key (let's call it normalization - the act of converting letter to number in the range 0-25 denoting it's position in alphabet) according to that.
if a.isalpha() and a.islower():
x = ((ord(text[i]) + ord(key[i])-97) % 26 + 97)
Here you take raw ^ ^
ascii value instead of | |
normalized (0-25) number |
|
Here you normalize the key according to the
message case
Following this are some heavy spoilers, so if you understand and want to solve the problem yourself, then stop reading here.
I'd suggest to separate the normalization and encryption steps, to avoid the confusion. Let's get rid of the special characters first instead of last, because it's easy to do and we'll only have to worry about letters:
if not (text[i].isalpha()):
cipher_text.append(text[i])
continue; # done with this symbol - skip to the next one
Then normalize the letters, with the help of built in method. Let's use variables p for plaintext, k for key and c for ciphertext (later):
p = ord(text[i].lower()) - ord('a') # converts to lowercase, then to number
k = ord(key[i].lower()) - ord('a') # both key AND plaintext
I used ord('a') instead of 65 as I'm used to that and find it clearer, but it is a matter of preference and language traditions (which I'm not accustomed with).
Then the encryption step:
c = (p + k) % 26;
And now to restore the capitalization. We destroyed it in the variables p and k, but we still have their source array text[] with intact values. We can use that to restore the capitalization (assuming the case should be inherited from plaintext):
if (text[i].islower()):
cipher_text.append(chr(c + ord('a')))
elif (text[i].isupper()):
cipher_text.append(chr(c + ord('A')))
That completes the main loop. Feel free to modify it to be more python compliant.
for i in range(len(text)):
if not (text[i].isalpha()):
cipher_text.append(text[i])
continue;
p = ord(text[i].lower()) - ord('a')
k = ord(key[i].lower()) - ord('a')
# k = ord(key[i % len(key)].lower()) - ord('a')
# if you skipped the key lengthening process
c = (p + k) % 26;
if (text[i].islower()):
cipher_text.append(chr(c + ord('a')))
elif (text[i].isupper()):
cipher_text.append(chr(c + ord('A')))
else:
# not sure if this can happen in python, but if it does, we're probably
# in trouble
# otherwise it might as well be merged with the elif above
return("" . join(cipher_text))

Related

How to start again at the beginning of the word?

To apply a Vigenere coding, we have to shift the letters but not all by the same number. The key is this time a keyword which each letter gives us the shift to be done (taking A for a shift of 0, B for a shift of 1 ...).
Let's take an example to explain the method: Let's imagine that the keyword is "MATHS" and the word to code is "PYTHON".
To code P, I shift the number corresponding to M, i.e. 12 (because we start at 0 with A) which gives me B as coding for P.
Let's move on to Y: I shift it by the number corresponding to A, i.e. 0, so Y is the coding for Y here.
Let's go to T which is shifted by the number corresponding to T, i.e. 19, so T becomes M once shifted
And so on.
import string
def vigenere_cipher(msg, shift):
encrypted = ''
for i,j in zip(msg,shift):
new_index = ( string.ascii_uppercase.index(i) + string.ascii_uppercase.index(j) ) % 26
encrypted += string.ascii_uppercase[new_index]
return encrypted
print(vigenere_cipher('PYTHON', 'MATH'))
If our keyword is too short we start again at the beginning of the word, i.e. N will be shifted by the number corresponding to M.
My problem right here is actually with the last part, How I can simply say that if the keyword is too short we start again at the beginning of the word ?
Because only "PYTH" part is encrypted to "BYMO" with MATH as a key but not the "ON"
I think the main issue here is that you're zipping both msg and shift together, when you don't actually need to do so. You already understand the concept of using % to guarantee that you stay on a number smaller than your max number, so I'll modify your function to also use % to select which character from shift you want to use
import string
def vigenere_cipher(msg, shift):
encrypted = ''
shift_length = len(shift)
for i, char in enumerate(msg):
new_index = ( string.ascii_uppercase.index(char) + string.ascii_uppercase.index(shift[i % shift_length]) ) % 26
encrypted += string.ascii_uppercase[new_index]
return encrypted
print(vigenere_cipher('PYTHON', 'MATH'))
Just add the line shift = shift * (len(msg) // len(shift) + 1) at the start of the function so shift is repeated until it's longer than msg (e.g. this line turns MATH into MATHMATH)
import string
def vigenere_cipher(msg, shift):
shift = shift * (len(msg) // len(shift) + 1)
encrypted = ''
for i,j in zip(msg,shift):
new_index = (string.ascii_uppercase.index(i) + string.ascii_uppercase.index(j)) % 26
encrypted += string.ascii_uppercase[new_index]
return encrypted
print(vigenere_cipher('PYTHON', 'MATH'))
Output: BYMOAN

Why it skips the last character of string

I have a problem in this code.
Why it skips the last character of my string. Lets say I want to calculate a hash for "timeGetTime", the loop stops at 'e' character.
I tried to print the API name and it prints all characters correctly.
Edit:
correct hash is 0xFF407C2F but the output is 0xE07FFA03
def ROR(data, shift, size=32):
shift %= size
body = data >> shift
remains = (data << (size - shift)) - (body << size)
return (body + remains)
def calculateHash(apiName):
result = 0
for ch in apiName:
result = ROR(result, 0x13)
result += ord(ch)
return result
You need to change the order of the lines so that the last letter also gets the results of the other function -
for ch in apiName:
result += ord(ch)
result = ROR(result, 0x13) # What you did was change the result later

Better way to test for a value in a list than to stack try's and except's

My code is implementing a Caesar Cipher, and this function is supposed to convert the inputted string, into an encrypted string.
To do this, I have to search for the character in 2 lists, a list of lowercase letters, uppercase letters, and then if it is not a letter, just add the character to the encrypted string.
I decided to use two layers of try's and except's to do this. Is there a better way I could do it, maybe with if/else?
import string
from tkinter import *
from tkinter import ttk
alphaLower = string.ascii_lowercase
alphaUpper = string.ascii_uppercase
alphaShiftL = alphaLower
alphaShiftU = alphaUpper
def shiftList(amount):
global alphaShiftL
global alphaShiftU
alphaShiftL = alphaLower[amount:] + alphaShiftL[:amount]
alphaShiftU = alphaUpper[amount:] + alphaShiftU[:amount]
def encrypt(unencrypted):
encrypted = ''
for char in unencrypted:
#HERE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
try:
alphaLower.index(char)
encrypted += alphaShiftL[alphaLower.index(char)]
except ValueError:
try:
encrypted += alphaShiftU[alphaUpper.index(char)]
except ValueError:
encrypted += char
return encrypted
There are more efficient ways to implement this -- But working with what you've got, the first thing I might recommend is to concatenate the lists so that you only need 1 try/except:
alpha = alphaLower + alphaUpper
alphaShift = alphaShiftL + alphaShiftU
for char in unencrypted:
try:
encrypted += alphaShift[alpha.index(char)]
except ValueError:
encrypted += char
Now we've only got a single try/except which is nice.
We can do better -- much better -- if we continue working with only a single object that contains the mapping. In this case, what you really want to do is to map a character in alpha to a different character in alphaShift. We can do this trivially with python dict:
mapping = dict(zip(alpha, alphaShift))
for char in unencrypted:
encrypted += mapping.get(char, char)
Or, more efficiently:
mapping = dict(zip(alpha, alphaShift))
encrypted = ''.join(mapping.get(char, char) for char in unencrypted)
To "decrypt", you just need the inverse mapping:
inverse_mapping = dict(zip(alphaShift, alpha))
unencrypted = ''.join(inverse_mapping.get(char, char) for char in encrypted)
if char.islower():
encrypted += alphaShiftL[alphaLower.index(char)]
elif char.isupper():
encrypted += alphaShiftU[alphaUpper.index(char)]
else:
encrypted += char
See documentation here and here.
If I understand correctly, you just need to know if something is present in a list. You can test for collection inclusion with this:
if search in collection:
ix = collection.index(search)
else:
//handle no
Did I understand you correctly?
Edit: mgilson's solution because you need the index of the element.
ix = collection.find(search)
if ix != -1:
//handle yes
else:
//handle no
Unless this is an academic exercise, I don't see why you're organizing your code this way. If you feel passionately about keeping the information in arrays, then you can just concatenate them together so you can get by with one lookup.
alphaLower = string.ascii_lowercase
alphaUpper = string.ascii_uppercase
alpha = alphaLower + alphaUpper
alphaShift = alpha
def shiftList(amount):
global alphaShiftL
global alphaShiftU
alphaShiftL = alphaLower[amount:] + alphaShiftL[:amount]
alphaShiftU = alphaUpper[amount:] + alphaShiftU[:amount]
alphaShift = alphaShiftL + alphaShiftU
Now you can do your lookups directly:
def encrypt(unencrypted):
encrypted = ''
for char in unencrypted:
idx = alpha.find(char)
encrypted += alphaShift[idx] if idx >= 0 else char
return encrypted
However, it would be far better to just use a dictionary:
lc = string.ascii_lowercase
uc = string.ascii_uppercase
cipher = dict(zip(lc+uc, lc[13:]+lc[:13]+uc[13:]+uc[:13]))
def encrypt(input):
return ''.join(cipher.get(c,c) for c in input)

Python Simple Encryption program: How to loop Z back to A

I'm trying to make an encryption function that encrypts plaintext messages but the problem is that if i input a key too large that goes past 'Z' then it goes onto greater unicode values.
My code:
def encrypt(var1,var2):
var3 = ""
for i in range(0, len(var1)):
if ord(var1[i])>64 and ord(var1[i])<90:
var3=var3+chr((ord(var1[i])+var2))
elif ord(var1[i])+var2>90:
???
else:
continue
return(var3)
How do I get it to loop 'Z' back to 'A'. I think I have to make an if statement like this but I'm not sure what to put into it.
elif ord(var1[i])+var2>90:
???
Here is my one! Im using the modulus operator to wrap around every 26 numbers (the number of letter between a-z). I also handle upper on lowercase separately.
def encrypt(data, shift):
result = ''
for c in data:
c_num = ord(c)
# is the letter lower case a - z?
if (c_num >= ord('a')) and (c_num <= ord('z')):
# get the letter number from 0 - 26
c_num = c_num - ord('a')
# shift the number
c_num += shift
# wrap the number every 26 numbers
c_num = c_num % 26
# now increase a by the new amount
c_num += ord('a')
result += chr(c_num)
# is the letter upper case A - Z?
elif (c_num >= ord('A')) and (c_num <= ord('Z')):
# get the letter number from 0 - 26
c_num = c_num - ord('A')
# shift the number
c_num += shift
# wrap the number every 26 numbers
c_num = c_num % 26
# now increase a by the new amount
c_num += ord('A')
result += chr(c_num)
return result
encrypt('aAbB', 2)
'cCdD'
encrypt('afZz', 2)
'chBb'
Here is the code golf version using list comprehension just for fun!
def encrypt(data, shift):
return ''.join([chr(((ord(c) - ord('a') + shift) % 26) + ord('a')) if ord(c) in range(ord('a'), ord('z')+1) else chr(((ord(c) - ord('A') + shift) % 26) + ord('A')) for c in data])
A straight-forward way would be to check if you have passed beyond Z, and modify the character in that case:
...
if var1[i] >= 'A' and var1[i] <= 'Z':
translated_char = chr(ord(var1[i])+var2)
if translated_char > 'Z':
# If the resulting character is beyond Z,
# we go 26 characters back
translated_char = chr(ord(translated_char)-26)
# Append the translated character to the output string
var3 += translated_char
...
You may want to consider more descriptive variable names -- you'll thank yourself if you revisit your code after two months :-)
I would recommend using the modulus operator to do what you are wanting. In python that is the % character. In modulus math. X % Y tells us what the remainder of X / Y is. For example. 27 % 26 is 1. Using this you can get your wrap around that you want. Here is a sample bit of code to encrypt a single character
def encrypt_character( valToEncrypt, keyVal ):
# Update the character to be our standard Alphabet mapping
# A -> 0; B->1 ... Z -> 25
x = ord(valToEncrypt) - ord('A')
# Perform the Encryption
retVal = ( x + keyVal ) % 26
# Translate back to the standard ASCII mapping of the character
# for display in python and translate it back into a string
retVal = chr(retVal + ord('A'))
return retVal
# end encrypt_character
Now if we feed the character "A" Into our encryption algorithm with a key of 13 we get "N" as shown:
>>> encrypt_character("A", 13)
'N'
The decrypt algorithm is very similar, except you do subtraction instead of addtion
def decrypt_character( valToDecrypt, keyVal ):
# Update the character to be our standard Alphabet mapping
# A -> 0; B->1 ... Z -> 25
x = ord(valToDecrypt) - ord('A')
retVal = ( x - keyVal ) % 26
# Translate back to the standard ASCII mapping of the character
# for display in python and translate it back into a string
retVal = chr(retVal + ord('A'))
return retVal
To encrypt a string you can use the following function:
from re import sub
def encrypt_message( message, key ):
# Convert the message text into a plain text with all spaces and
# punctuation removed.
plainText = sub(r'[^A-Z]', '', message.upper())
cipherText = ""
charIndex = 0
# Encrypt the message 1 character at a time
while charIndex < len(plainText):
cipherText += \
encrypt_character( plainText[charIndex], key)
charIndex += 1
return cipherText
This function can be called:
>>> encrypt_message("HELLO World!", key=23)
'EBIILTLOIA'
The decryption function is very similar to the encryption function, except it calls the decrypt utility instead of the encrypt utility.

Decoding an encrypted file using Caesar Cipher

I want to decrypt an encrypted file. I'm having trouble all the way at the bottom when converting it and comparing it to a dictionary (which is full of words). Can someone guide me in the right direction? I'm struggling comparing the two.
#this function takes a string and encrypts ONLY letters by k shifts
def CaeserCipher(string, k):
#setting up variables to move through
upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'*10000
lower = 'abcdefghijklmnopqrstuvwxyz'*10000
newCipher = ''
#looping each letter and moving it k times
for letter in string:
if letter in upper:
if upper.index(letter) + k > 25:
indexPosition = (upper.index(letter) + k)
newCipher = newCipher + upper[indexPosition]
else:
indexPosition = upper.index(letter) + k
newCipher = newCipher + upper[indexPosition]
elif letter in lower:
if lower.index(letter) + k > 25:
indexPosition = (lower.index(letter) + k)
newCipher = newCipher + lower[indexPosition]
else:
indexPosition = lower.index(letter) + k
newCipher = newCipher + lower[indexPosition]
else:
newCipher = newCipher + letter
return newCipher
f = open('dictionary.txt', "r")
dictionary = set()
for line in f:
word = line.strip()
dictionary.add(word)
print dictionary
#main file
#reading file and encrypting text
f = open('encryptMystery1.txt')
string = ''
out = open("plain1.txt", "w")
myList = []
for line in f:
myList.append(line)
for sentence in myList:
for k in range(26):
updatedSentence = CaeserCipher(sentence, k)
for word in updatedSentence.split():
if word in dictionary:
out.write(updatedSentence)
break
print myList
f.close()
out.close()
Let's tackle this in steps, and the first step is entitled
WHY DO YOU HAVE 260,000 CHARACTER LONG STRINGS IN A CAESAR CIPHER
Sorry, I don't mean to be overly dramatic, but you realize that's going to take up more space than, well, Space, don't you? And it's completely unnecessary. It's an ugly and slow hack to avoid understanding the % (modulo) operator. Don't do that.
Now, to the modulo:
Step two of course will have to be understanding the modulo. It's not actually hard, it's just like the remainder of a division problem. You remember when you were in school and just LEARNING division? 7/4 was 1r3 not 1.75, remember? Well Python has functions for all that. 7/4 == 1.75, 7//4 == 1 and 7 % 4 == 3. This is useful because it can serve to "wrap" a number around a fixed length.
Let's say for example you have some string with 26 indexes (like, I don't know, an alphabet?). You're trying to add some number to a starting index, then return the result but UGH YOU'RE ADDING 2 TO Y AND IT DOESN'T WORK! Well with modulo it can. Y is in index 24 (remember zero is its own index), and 24+2 is 26 and there IS no 26th index. However, if you know there's going to be only 26 elements in your string, we can take the modulo and use THAT instead.
By that logic, index + CONSTANT % len(alphabet) will ALWAYS return the right number using simple math and not sweet baby jesus the quarter million element long string you just butchered.
Ugh your mother would be ashamed.
Reversing a Caesar cipher
So you've got a good idea, going through each line in turn and applying every kind of cipher to it. If I were you I'd dump them all into separate files, or even into separate list elements. Remember though that if you're reversing the cipher, you need to use -k not k. It's probably a good idea to simply change your Caesar cipher to detect that though, since the modulo trick doesn't work in this case. Try something like:
def cipher(text, k):
cipherkey = "SOMESTRINGGOESHERE"
if k < 0:
k = len(cipherkey) + k
# len(cipherkey) - abs(k) would be more clear, but if it HAS to be
# a negative number to get in here, it seems silly to add the call
# to abs
Then you can do:
startingtext = "Encrypted_text_goes_here"
possibledecrypts = [cipher(startingtext, -i) for i in range(1,26)]

Categories