Review

  • Rules of Programming
  • Cryptography

Rules of Programming

Rule 0: Don't panic

Rule 1: Think before you program!

Rule 2: A program is a human-readable essay on problem solving that also happens to execute on a computer.

Rule 3: The best way to improve your programming and problem skills is to practice.

Cryptography

  • The study of using secret codes
  • Encryption is a method of scrambling input text using a cipher key to create cipher text

Caesar Cipher

  • Shift alphabet by X characters
  • Cipher key = x

In [ ]:
alphabet = "abcdefghijklmnopqrstuvwxyz"
input_text = "twas brillig in the slithy tove"
cipher_key = 15

cipher_text = ""

for character in input_text:
    character_position = alphabet.find(character)
    new_position = character_position + cipher_key
    if new_position >= len(alphabet):
        new_position = new_position - len(alphabet)
    new_letter = alphabet[new_position]
    cipher_text = cipher_text + new_letter
    print(cipher_text)

Variables

  • Variables are like containers. They have a label (a variable name) and can hold a value.

Here is a variable that contains a string.

In [ ]:
jabberwocky = "Did gyre and gimble in the wabe:"
print(jabberwocky)

Indexing

  • Individual characters in strings can be identified using an index.
  • This is done by adding square brackets to the end of a string (value or variable)
  • Indexes start from 0 (It's a computer science thing.)

In [ ]:
input_text = "all mimsy were the borogoves"
input_text[0] # This should be a

cipher_key = 15

alphabet = "abcdefghijklmnopqrstuvwxyz"

print(alphabet[0 + cipher_key])
print(alphabet[1 + cipher_key])
print(alphabet[2 + cipher_key])
print(alphabet[3 + cipher_key])

Looking Up a Character in a String

  • Python is an object-oriented language. This means we can ask questions of objects.
  • Strings are objects.
  • Ask a string to find where a letter occurs.

.find(letter)

In [ ]:
alphabet = "abcdefghijklmnopqrstuvwxyz"
input_text = "and the mome raths outgrabe"
cipher_key = 3

character = input_text[0]
character_position = alphabet.find(character)
new_position = character_position + cipher_key
new_letter = alphabet[new_position]
print(new_letter)

character = input_text[1]
character_position = alphabet.find(character)
new_position = character_position + cipher_key
new_letter = alphabet[new_position]
print(new_letter)

character = input_text[2]
character_position = alphabet.find(character)
new_position = character_position + cipher_key
new_letter = alphabet[new_position]
print(new_letter)

character = input_text[3]
character_position = alphabet.find(character)
new_position = character_position + cipher_key
new_letter = alphabet[new_position]
print(new_letter)

String Concatenation

  • Two strings can be added together using the + operator. This is called string concatenation.
  • Python concatenates the strings exactly. It doesn't add extra spaces.
In [ ]:
print("Beware the Jabberwock," + " " + "my son!")

spam = "email"
eggs = "bacon"

print (spam + eggs)

Decision Structures

So far, we've been writing programs that do the same thing repeatedly. If order for a program to be more reactive, we need a way for it to make decisions. Depending on whether something is the case, it takes one action or another.

In [ ]:
temperature = 10

if (temperature < 5):
    print("Wear a coat.")

Boolean Expressions and Relational Operators

We can use decision structures to adjust string indexes that are too big or too small.

if the index is too big then ...

In [ ]:
alphabet = "abcdefghijklmnopqrstuvwxyz "

index = 25

print(len(alphabet))
print(str(index) + ": " + str(alphabet[index]))

index = index + 1
print(str(index) + ": " + str(alphabet[index]))

index = index + 1
# print(alphabet[27])

if index >= len(alphabet):
    index = index - len(alphabet)

print(str(index) + ": " + str(alphabet[index]))

index = index + 1
if index >= len(alphabet):
    index = index - len(alphabet)

print(str(index) + ": " + str(alphabet[index]))

Repetition Structures

So far, we have been repeating actions by repeating lines of code.

If we are going to encrypt longer strings, we need to make this easier.

This is where repetition structures come in.

In [ ]:
for number in [1, 2, 3, 4]:
    print(number)

for character in "The jaws that bite, the claws that catch!":
    print(character)

More Characters

So far, the program can only handle lowercase characters. We can expand the set of characters to include:

  • Uppercase
  • Digits
  • Punctuation

But we'll probably run into something that's not yet in our alphabet.

How can we fix this?

In [1]:
alphabet = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
input_text = "$@Twas brillig in the slithy tove"
cipher_key = 1

cipher_text = ""

for character in input_text:
    if character in alphabet:
        character_position = alphabet.find(character)
        new_position = character_position + cipher_key
        if new_position >= len(alphabet):
            new_position = new_position - len(alphabet)
        new_letter = alphabet[new_position]
        cipher_text = cipher_text + new_letter
    else:
        cipher_text = cipher_text + character
    
print(cipher_text)
$@UxbtAcsjmmjhAjoAuifAtmjuizAupwf

Input

  • The current program requires the input string and cipher key to be assigned to a variable.
  • This is called "hard coding". It's considered to be a bad practice, because computer programs are fragile. The program needs to be changed to handle different inputs.
  • Let's change this.

  • Let's start with the last one.

    • variable = input(prompt) or variable = raw_input(prompt)
    • input() is a function, like print()
    • prompt is the string to be displayed on the screen
    • variable holds the data input from the keyboard
In [15]:
sentence = raw_input("Type in a sentence: ")
number = int(raw_input("Enter a number between 0 and 25: "))
Type in a sentence: This is your name.
Enter a number between 0 and 25: 3
In [ ]:
alphabet = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
input_text = raw_input("Enter your input text: ")
cipher_key = raw_input("Enter a number between 0 and " + str(len(alphabet)) + ": ")
cipher_key = int(cipher_key)

cipher_text = ""

for character in input_text:
    if character in alphabet:
        character_position = alphabet.find(character)
        new_position = character_position + cipher_key
        if new_position >= len(alphabet):
            new_position = new_position - len(alphabet)
        new_letter = alphabet[new_position]
        cipher_text = cipher_text + new_letter
    else:
        cipher_text = cipher_text + character
    
print(cipher_text)

Decryption

Decrypting the Caesar cipher is the same as the encryption process but in reverse.

Let's try doing this on paper.

Exchange a cipher text and cipher key with someone else. Decrypt the cipher text.

In [ ]:
alphabet = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
input_text = raw_input("Enter your input text: ")
cipher_key = raw_input("Enter a number between 0 and " + str(len(alphabet)) + ": ")
cipher_key = int(cipher_key)

cipher_text = ""

for character in input_text:
    if character in alphabet:
        character_position = alphabet.find(character)
        new_position = character_position + cipher_key
        if new_position >= len(alphabet):
            new_position = new_position - len(alphabet)
        new_letter = alphabet[new_position]
        cipher_text = cipher_text + new_letter
    else:
        cipher_text = cipher_text + character
    
print(cipher_text)

Decrypting Using the Program

In [22]:
alphabet = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
direction = raw_input("Encrypt or decrypt? (d/e): ")
input_text = raw_input("Enter your input text: ")
cipher_key = raw_input("Enter a number between 0 and " + str(len(alphabet)) + ": ")
cipher_key = int(cipher_key)

if direction == "d":
    cipher_key = -cipher_key

cipher_text = ""

for character in input_text:
    if character in alphabet:
        character_position = alphabet.find(character)
        new_position = character_position + cipher_key
        if new_position >= len(alphabet):
            new_position = new_position - len(alphabet)
        new_letter = alphabet[new_position]
        cipher_text = cipher_text + new_letter
    else:
        cipher_text = cipher_text + character
    
print(cipher_text)
Encrypt or decrypt? (d/e): e
Enter your input text: It's the WEEKEND!!!
Enter a number between 0 and 63: 1
Ju'tAuifAXFFLFOE!!!

Breaking an Encryption

  • Modern methods of breaking an encryption are based on statistical techniques.

    • For example, the frequency of characters (e.g. E and T are most common) or words ("the," "be," "and")
  • But the Caesar cipher is a pretty simple linear cipher. We can try using "brute force"

  • A brute force technique involves trying every cipher key.

Let's write a breaking program using the existing code as a starting point.

In [ ]:
 

Functions

  • A function is a way to group together lines of code.
  • Useful for doing the same task in different places.
  • Makes the code easier to read.
  • Avoids repeating code.
def function_name():
    statement
    statement
    statement
  • A function has the following parts.
    • A name
    • Parameters (inputs)
    • Optionally, return values (outputs)
In [24]:
# This program demonstrates a function
# First we define a function named "message"

def message():
    print("I am Arthur")
    print ("King of the Britons")

# Call the message function
message()
I am Arthur
King of the Britons
I am Arthur
King of the Britons

Passing Data to Functions

  • An argument is data passed in to a function when it is called.
  • A parameter is a variable that receives the data. It is named when the code is written.

Return Values

  • Data that is passed back to the calling context
In [28]:
def holy_hand_grenade(tick):
    output = ""
    
    if tick == 1:
        output = "Keepest going"
    elif tick == 2:
        output = "Nor either count thou two, excepting that thou then proceed to three"
    elif tick == 3:
        output = "Lobbest thou thy Holy Hand Grenade of Antioch towards thou foe"
    elif tick == 4:
        output = "Four shalt thou not count"
    elif tick == 5:
        output = "Five is right out"
    
    return output

holy_hand_grenade(3)
Out[28]:
'Lobbest thou thy Holy Hand Grenade of Antioch towards thou foe'

Let's Practice

In [32]:
# Turn this code into a function
# Input? temperature
# Processing? decision
# Output? string

input_temp = 10

def what_to_wear(temperature):
    output = ""
    
    if (temperature < 15):
        output = "Wear a coat."
    
    return output

attire = what_to_wear(input_temp)
print(attire)
Wear a coat.

Putting It Together

  • Let's put our decryption code into a function
  • Call it repeatedly with possible cipher keys
In [2]:
alphabet = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
input_string = raw_input("Enter your input text: ")


def decrypt(input_text, cipher_key):
    cipher_text = ""
    for character in input_text:
        if character in alphabet:
            character_position = alphabet.find(character)
            new_position = character_position - cipher_key
            if new_position >= len(alphabet):
                new_position = new_position - len(alphabet)
            new_letter = alphabet[new_position]
            cipher_text = cipher_text + new_letter
        else:
            cipher_text = cipher_text + character

    return cipher_text

for key in range(len(alphabet)):
    possible_string = decrypt(input_string, key)
    print(possible_string)
Enter your input text: $@UxbtAcsjmmjhAjoAuifAtmjuizAupwf
$@UxbtAcsjmmjhAjoAuifAtmjuizAupwf
$@Twas brillig in the slithy tove
$@Sv9rzaqhkkhfzhmzsgdzrkhsgxzsnud
$@Ru8qy9pgjjgeyglyrfcyqjgrfwyrmtc
$@Qt7px8ofiifdxfkxqebxpifqevxqlsb
$@Ps6ow7nehhecwejwpdawohepduwpkra
$@Or5nv6mdggdbvdivoc9vngdoctvojq9
$@Nq4mu5lcffcauchunb8umfcnbsunip8
$@Mp3lt4kbeeb9tbgtma7tlebmartmho7
$@Lo2ks3jadda8safsl96skdal9qslgn6
$@Kn1jr2i9cc97r9erk85rjc9k8prkfm5
$@Jm0iq1h8bb86q8dqj74qib8j7oqjel4
$@IlZhp0g7aa75p7cpi63pha7i6npidk3
$@HkYgoZf69964o6boh52og96h5mohcj2
$@GjXfnYe58853n5ang41nf85g4lngbi1
$@FiWemXd47742m49mf30me74f3kmfah0
$@EhVdlWc36631l38le2Zld63e2jle9gZ
$@DgUckVb25520k27kd1Ykc52d1ikd8fY
$@CfTbjUa1441Zj16jc0Xjb41c0hjc7eX
$@BeSaiT90330Yi05ibZWia30bZgib6dW
$@AdR9hS8Z22ZXhZ4haYVh92ZaYfha5cV
$@ cQ8gR7Y11YWgY3g9XUg81Y9Xeg94bU
$@zbP7fQ6X00XVfX2f8WTf70X8Wdf83aT
$@yaO6eP5WZZWUeW1e7VSe6ZW7Vce729S
$@x9N5dO4VYYVTdV0d6URd5YV6Ubd618R
$@w8M4cN3UXXUScUZc5TQc4XU5Tac507Q
$@v7L3bM2TWWTRbTYb4SPb3WT4S9b4Z6P
$@u6K2aL1SVVSQaSXa3ROa2VS3R8a3Y5O
$@t5J19K0RUURP9RW92QN91UR2Q792X4N
$@s4I08JZQTTQO8QV81PM80TQ1P681W3M
$@r3HZ7IYPSSPN7PU70OL7ZSP0O570V2L
$@q2GY6HXORROM6OT6ZNK6YROZN46ZU1K
$@p1FX5GWNQQNL5NS5YMJ5XQNYM35YT0J
$@o0EW4FVMPPMK4MR4XLI4WPMXL24XSZI
$@nZDV3EULOOLJ3LQ3WKH3VOLWK13WRYH
$@mYCU2DTKNNKI2KP2VJG2UNKVJ02VQXG
$@lXBT1CSJMMJH1JO1UIF1TMJUIZ1UPWF
$@kWAS0BRILLIG0IN0THE0SLITHY0TOVE
$@jV RZAQHKKHFZHMZSGDZRKHSGXZSNUD
$@iUzQY PGJJGEYGLYRFCYQJGRFWYRMTC
$@hTyPXzOFIIFDXFKXQEBXPIFQEVXQLSB
$@gSxOWyNEHHECWEJWPDAWOHEPDUWPKRA
$@fRwNVxMDGGDBVDIVOC VNGDOCTVOJQ 
$@eQvMUwLCFFCAUCHUNBzUMFCNBSUNIPz
$@dPuLTvKBEEB TBGTMAyTLEBMARTMHOy
$@cOtKSuJADDAzSAFSL xSKDAL QSLGNx
$@bNsJRtI CC yR ERKzwRJC KzPRKFMw
$@aMrIQsHzBBzxQzDQJyvQIBzJyOQJELv
$@9LqHPrGyAAywPyCPIxuPHAyIxNPIDKu
$@8KpGOqFx  xvOxBOHwtOG xHwMOHCJt
$@7JoFNpEwzzwuNwANGvsNFzwGvLNGBIs
$@6InEMoDvyyvtMv MFurMEyvFuKMFAHr
$@5HmDLnCuxxusLuzLEtqLDxuEtJLE Gq
$@4GlCKmBtwwtrKtyKDspKCwtDsIKDzFp
$@3FkBJlAsvvsqJsxJCroJBvsCrHJCyEo
$@2EjAIk ruurpIrwIBqnIAurBqGIBxDn
$@1Di HjzqttqoHqvHApmH tqApFHAwCm
$@0ChzGiypsspnGpuG olGzsp oEG vBl
$@ZBgyFhxorromFotFznkFyroznDFzuAk
$@YAfxEgwnqqnlEnsEymjExqnymCEyt j
$@X ewDfvmppmkDmrDxliDwpmxlBDxszi
$@WzdvCeulooljClqCwkhCvolwkACwryh
$@VycuBdtknnkiBkpBvjgBunkvj Bvqxg
In [39]:
for num in range(10):
    print(num)
0
1
2
3
4
5
6
7
8
9

Last Words on Cryptography

  • Cryptography is like a dance of "yes" and "no."
  • As encryption algorithms get better, so do the algorithms to break them.
  • But you've learned a lot about basic principles
  • Improvements are based on increasing the number of combinations to try and/or changing the frequency of letters/words in the output

Improvements on Caesar Cipher

  • Polyalphabetic ciphers encode the same character in a different ways.
    • In our code, it would be like using multiple alphabets and cycling through them
  • Encryption keys can be longer
    • A key can point to a cycle of keys
  • Non-linear ciphers group code into blocks and rotated before encryption
    • Four characters becomes a 2x2 block
    • Can be repeated

Computational Thinking

The ability to say "I can write a computer program to solve that problem."