Caesar Cipher Python Project

Viewed 84

I'm trying to do a simple Caesar Cipher in Python. My encryption works, but my decryption function gives the error message that the string index is out of range. I've attached my code. I'm welcome to any comments.

import random

symbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!?@#$%^&*()' " 

def encrypt_symbols(message, key):

    encrypted = ''
    index = 0
    for letter in message:
        index = symbols.find(letter) + key
        if index > len(symbols):
            index = index - (len(symbols) + 1)
        encrypted = encrypted + symbols[index] 

    return encrypted



def decrypt(message, key):

    decrypted = ''
    index = 0
    for letter in message:
        index = symbols.find(letter) - key
        if index < len(symbols):
            index = index + len(symbols)
        decrypted = decrypted + symbols[index]

    return decrypted

test = "Wow! This works well, don't you think so?"
key = random.randrange(1, len(symbols))

secret_message = encrypt_symbols(test, key)

print(encrypt_symbols(test, key))

print(decrypt(secret_message, key))
3 Answers

let's say that your symbol[letter] is 'a' and the key is 2 then , your encryption will give you 'a' + 2 = 'c' which is correct

but for decryption index = symbols.find('c') - key = 1 but then you add the length of symbol to index and go out of range

if index < len(symbols)
 index = index + len(symbols) 

and you actually go out of range you may do instead of if index < len(symbols)

if index < 0

Firstly, you need to replace

index = index - (len(symbols) + 1)

with

index = index - (len(symbols)) 

in your encrypt_symbols function and after this replace you decrypt function with this code:

def decrypt(message, key):
    decrypted = ''
    index = 0
    for letter in message:
        index = symbols.find(letter) - key
        if index < len(symbols):
            index = index + (len(symbols))
            index = (index%len(symbols))
        decrypted = decrypted + symbols[index]

    return decrypted

Here I have used modulus(%) in order to get the remainder for the respective letter of secret_message.

Moreover, the best practice to replace the letter is using modulus and avoiding Addition/Substraction.

Code for your reference using Modulus:

def encrypt_symbols(message, key):
    encrypted = ''
    index = 0
    for letter in message:
        index = symbols.find(letter) + key
        if index > len(symbols):
            index = (index%len(symbols))
        encrypted = encrypted + symbols[index] 
    return encrypted

and

def decrypt(message, key):
    decrypted = ''
    index = 0
    for letter in message:
        index = symbols.find(letter) - key
        if index < len(symbols):
            index = (index%len(symbols))
        decrypted = decrypted + symbols[index]
    return decrypted

Make sure to include all the symbols, else find will replace it eith space.

I have use the following

symbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!?@#$%^&*()', "

The big problem is not understanding group / modulus operation and some one-off errors.


In the encryption operation:

if index > len(symbols):

Indices start at zero, so if you have the normal ABC alphabet with 26 characters then Z, the character with the highest index would be at index 25. In the above code there is a highest index of 26 assumed; you'd need to use >=.

index = index - (len(symbols) + 1)

I've a strong idea that this was to make up for the previous mistake, but failed. Any operations in a group of size N will use modulo N operations. In other words, for the ABC you'd subtract N=26 here, not N=27, the + 1 should be removed (as well as a pair of parentheses as they are not needed anymore).


In the decryption operation:

if index < len(symbols):

This is the wrong assumption; indices in the range [0, N) are fine. You'd need to compare with zero here, as subtraction can lead to negative values.

index = index + len(symbols)

Now this is where the error occurs, as you now try to fix values that are already in the correct range. Fortunately you did correctly use len(symbols) instead of len(symbols) + 1.


Generally we program group operators using the modulus operator.

So you just need:

index = (symbols.find(letter) + key) % len(symbols)

during encryption, and:

index = (symbols.find(letter) - key) % len(symbols)

during decryption - without any if statements. But as you now have implemented group operations, you can also feed the encryption routine -key instead of key and get the decryption routine "for free".


Notes:

  • other (C-based) languages such as Java may have % represent a remainder, which may end up giving a negative result, so you'd have to use another mod function - Python fortunately uses an actual modulus operation;
  • if you are going to multiply or perform exponentiation in a group then you should be using functions such as modmul or modpow (i.e. pow() with 3 arguments in Python) for efficiency reasons.
Related