Tuesday, 26 September 2017

Caesar's Cipher in Python

Back in the olden days of the Roman Empire, cryptography and cryptanalysis was not as sophisticated as it is these days. So Julius Caesar's brilliant cipher consisted of taking each letter in the words he was encrypting and swapping it for the letter a certain number of steps ahead or behind in the alphabet. If he was shifting the letters by one step then H becomes I, E becomes F, L becomes M and O becomes P. Thus the word HELLO is encrypted as IFMMP. If you know the key, you decrypt by shifting each letter back by that many steps in the alphabet.

So here's a program I've done.
#!/usr/bin/python3.5
alphabet_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
print ("Please enter code setting (integer 1-25)")
shift = input("?")
shift = int(shift)
initialstring = input("Please enter plain text string: ")
initialstring = initialstring.lower()
shiftedstring = ""
for initialchar in initialstring:
    if (initialchar in alphabet_list):
        initialplace = alphabet_list.index(initialchar)
        shiftplace = initialplace + shift
        if (shiftplace > 25):
            shiftplace = shiftplace - 26
        elif (shiftplace < 0):
            shiftplace = shiftplace + 26
        shiftedchar = alphabet_list[shiftplace]
    else:
        shiftedchar = initialchar
    shiftedstring = shiftedstring + shiftedchar
print (shiftedstring)
This is longer than some previous scripts and there are few things I would like to point out, There are a number of functions and techniques I haven't mentioned before, and perhaps I am jumping ahead with this script :
  • At its heart is the list of alphabet letters, and increasing or decreasing the index does the job of changing the letter by a number of steps. It should be on one long line, but word-wrapping on this blog makes it appear to cover multiple lines.
  • initialstring = initialstring.lower() converts the string to lower case. This keeps things simple and I don't have to worry about a separate list for upper case letters. This function works on strings. 
  • The for loop treats the initialstring as a sequence of characters, similar to a list. Each time the loop repeats, it picks up the next character in the string until it reaches the end. 
  • if (initialchar in alphabet_list) checks whether or not the selected character is in the alphabet_list. Like other if...else... statements. if the statement is true then the lines below the if statement are run. Otherwise the line below the else: statement is run. 
  • alphabet_list.index(initialchar) returns the index (i.e. the position in the list) of the selected letter in the initialchar variable. We know that it will return one because the previous if statement just checked that it's there in the list. 
  • The deeply nested if...elif... statements are for when shifting along the list goes off either end. It sort of joins the ends the list so that z +1 gives a, and a -1 gives z. They use the < (less than) and > (greater than) comparators. 
  • shiftedstring = shiftedstring + shiftedchar is a demonstration of string concatentation. Just as with lists, you can use + to join two different strings together. Here it is adding each newly-encoded character onto the end of the encoded string. 
So what is the output?
>>>
 RESTART: C:\Users\John\Dropbox\Misc Programming\Python\python3\test05_rot13.py
Please enter code setting (integer 1-25)
?
3
Please enter plain text string: Hail Julius Caesar! And Hello World!
kdlo mxolxv fdhvdu! dqg khoor zruog!
>>>
 RESTART: C:\Users\John\Dropbox\Misc Programming\Python\python3\test05_rot13.py
Please enter code setting (integer 1-25)
?
23
Please enter plain text string: kdlo mxolxv fdhvdu! dqg khoor zruog!
hail julius caesar! and hello world!
>>>
The second run was to show how to decrypt - use the code setting of 26 minus the original code setting. This will reverse any letter changes from the original encryption but not restore lost capital letters.  You could also decrypt by using a code setting of the negative of the original code (so here -3).
Footnote: The code works fine with code settings of -52 up to 26, so actually the prompt for the code setting should be modified. Any number beyond that range will cause an out-of-range error.
I was puzzled that it should cope with such numbers of negative magnitude. Then I realised that it would be looking for an index of [-26] or higher. This is then interpreted by Python as start at the end of the list and work backwards. I don't know if I'm a subconscious genius or just lucky...

No comments:

Post a Comment