Hack My Planet

Day 18: Cryptography

Dictionary Exercise

  • Think of five programming or networking terms you've learned in this class. Use them as keys, and their definitions as values in a dictionary. Print each word and its meaning as neatly formatted output.
    • You might print the word followed by a colon and then its meaning, or print the word on one line and its meaning indented on a second line (use the newline character \n to insert a blank line)

Cryptography

  • Cryptography is a way of disguising the contents of your message, to make it harder for your enemies to read.
    • Imagine Alice wants to send a secret message to Bob, without Eve knowing what the message says. Alice first picks a key, which will be a number such as 3. Alice then tells Bob the key.

      Whenever Alice wants to write a message, all she needs to do is shift each of the letters in her message forward in the alphabet by 3 places

      • So the plaintext: Meet me at the park at three

        becomes the ciphertext: Phhw ph dw wkh sdun dw wkuhh

One Time Pad

  • A one-time pad (OTP) is a different method of encryption. When using an OTP, a string of random numbers are generated and shared between Alice and Bob. Each letter of the message is then shifted by the corresponding number in the OTP, so each letter has its own individual key! As long as Eve doesn't have the OTP, the message is impossible to decrypt.

Generating an OTP

  • Make a new Python file in your python_work directory
  • Import the randint function from the random module
    • from random import randint
  • You'll need the alphabet
    • ALPHABET = 'abcdefghijklmnopqrstuvwxyz'
  • Create a function to generate the OTP, parameters for the number of sheets in the pad, and the number of characters each sheet can encrypt
    • def generate_otp(sheets, length):
  • A new file needs to be created for every sheet. Each file will be saved as .txt. Ex: otp0.txt, otp1.txt, otp2.txt.
    • Add in two lines of code that will write out the random numbers to the file. \n so they're written to a new line

Generating an OTP

def generate_otp(sheets, length):
    for sheet in range(sheets):
        with open("otp" + str(sheet) + ".txt","w") as f:
            for i in range(length):
                f.write(str(randint(0,26))+"\n")
  • Test your code with generate_otp(5, 100) and run it.

 

  • If you open your file browser, you should see 5 new files have been created. Open one of them and you'll see a column of random numbers. These files are collectively called your one-time pad.

Loading a Sheet

  • You need a way to load a sheet and store the numbers in a list
  • Create a function to open a file and load the contents into a list by breaking up each line into its own item
    • def load_sheet(filename):
          with open(filename, "r") as f:
              contents = f.read().splitlines()
          return contents
  • Test your function by saving and running it with:

 

sheet = load_sheet('otp0.txt')

print(sheet)

Writing a Secret Message

The next function asks the user to type in the message that will be encrypted, and it converts all letters to lowercase

def get_plain_text():
    plain_text = input('Please type your message ')
    return plain_text.lower()

Loading & Saving

Now you'll need two functions to open messages written to you, and saving the encrypted messages

def load_file(filename):
    with open(filename, "r") as f:
        contents = f.read()
    return contents

def save_file(filename, data):
    with open(filename, 'w') as f:
        f.write(data)

Encrypting a Message

  • Encrypt a message from using a sheet from the OTP
  • Define a function with the message & used OTP sheet parameters and use an empty string to store the ciphertext
    • def encrypt(plaintext, sheet):
          ciphertext = ''
  • It will iterate through the string and use enumerate() to keep track of each character to encrypt and its position. Then it'll check if it's in the alphabet
def encrypt(plaintext, sheet):
    ciphertext = ''
    for position, character in enumerate(plaintext):
        if character not in ALPHABET:
            ciphertext += character

Encrypting a Message

  • Firstly, you need to find the position of the plaintext character in the alphabet
    • ALPHABET.index(character).
  • Then you need to add this number to the value from the equivalent position on the sheet from the OTP
    • int(sheet[position]).
  • This new number needs converted back into a letter. If the new number was 0 it would become a, if it was 5 it would become f and so on. If the number exceeds the limit of 25, it needs to reset (26 needs to be changed to 0, 30 should be changed to 4). To do this we can use the modulo operator (%).
  • Lastly, the number is converted to a letter, and the ciphertext is returned

Encrypting a Message

def encrypt(plaintext, sheet):
    ciphertext = ''
    for position, character in enumerate(plaintext):
        if character not in ALPHABET:
            ciphertext += character
        else:
            encrypted = (ALPHABET.index(character)
                        + int(sheet[position])) % 26
            ciphertext += ALPHABET[encrypted]
    return ciphertext

Save and test your function with:

sheet = load_sheet('otp0.txt')
print(encrypt('This is a secret message.', sheet))

Decrypting the Message

def decrypt(ciphertext, sheet):
    plaintext = ''
    for position, character in enumerate(ciphertext):
        if character not in ALPHABET:
            plaintext += character
        else:
            decrypted = (ALPHABET.index(character) 
                        - int(sheet[position])) % 26
            plaintext += ALPHABET[decrypted]
    return plaintext

Decrypting the message is similar to encrypting it. The only difference is you subtract the value rather than add it.

Decrypting the Message

Save and test your function with:

sheet = load_sheet('otp0.txt')
ciphertext = encrypt('Nobody can read this', sheet)
print(ciphertext)

print(decrypt(ciphertext, sheet))

Adding a Menu

  • Lets make the program easier to use for the people by including a menu
  • Give the user 4 choices in the menu
def menu():
    choices = ['1', '2', '3', '4']
    choice = '0'
    while True:
        while choice not in choices:
            print('What would you like to do?')
            print('1. Generate one-time pads')
            print('2. Encrypt a message')
            print('3. Decrypt a message')
            print('4. Quit the program')
            choice = input('Please type 1, 2, 3 or 4 and press Enter ')

Adding a Menu

  • If option 1 is chosen, then the user needs to be asked how many sheets they want to generate and how long the sheets should be
  • These are passed into the generate_otp() function
if choice == '1':
    sheets = int(input('How many one-time pads 
                         would you like to generate? '))
    length = int(input('What will be your maximum 
                         message length? '))
    generate_otp(sheets, length)

Adding a Menu

  • If option 2 is chosen, then you need to get the name of the sheet they wish to use and the message they want to write
  • The message can be encrypted and saved with a name of their choosing
elif choice == '2':
    filename = input('Type in the filename of 
                        the OTP you want to use ')
    sheet = load_sheet(filename)
    plaintext = get_plaintext()
    ciphertext = encrypt(plaintext, sheet)
    filename = input('What will be the name 
                        of the encrypted file? ')
    save_file(filename, ciphertext)

Adding a Menu

  • If option 3 is chosen, then you need to get the name of the sheet used to encrypt the file and the name for the file to be decrypted
  • The file can then be opened, decrypted, and the contents printed out
elif choice == '3':
    filename = input('Type in the filename of 
                       the OTP you want to use ')
    sheet = load_sheet(filename)
    filename = input('Type in the name of the 
                        file to be decrypted ')
    ciphertext = load_file(filename)
    plaintext = decrypt(ciphertext, sheet)
    print('The message reads:')
    print('')
    print(plaintext)

Adding a Menu

  • If option  is chosen, the program should exit
  • You need to reset the choice variable at the end of the function, so that the loop will continue around
  • Call the menu function to complete the code
elif choice == '4':
    exit()
choice = '0'
menu()

Function Exercise

  • Define a function reverse() that takes in a string and  returns the reversal of that string.
    • For example: reverse("This is a string") should return "gnirts a si sihT")

Hack My Planet Cryptography

By jtheadland

Hack My Planet Cryptography

  • 590