Skip to content

added submission for csmith95 #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions csmith95-assign1/caesar-cipher.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SBWKRQ
1 change: 1 addition & 0 deletions csmith95-assign1/caesar-plain.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PYTHON
300 changes: 300 additions & 0 deletions csmith95-assign1/crypto.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
"""
Assignment 1: Cryptography
Course: CS 92SI
Name: Conner Smith
Date: 10/9/15

This program encrypts/decrypts plaintext using either
Caesar's cipher, Vigenere's cipher, or railfence cipher

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good - remember that in Python, it's stylistically conventional to put function documentation comments inside of the function as the first comment, using the """comment""" syntax. Just a note :)

"""
import string

LETTERS_IN_ALPHABET = 26

# a dictionary of char-to-char mappings for
# Caesar's cipher
letterMappings = {}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to fill these dictionaries just once, when the program starts up? Right now, we're recreating these mappings every time a new suite is run.

reverseLetterMappings = {}

# returns character that originalLetter maps to given an offset. Assumes originalLetter
# is lowercase
def computeNewLetter(originalLetter, offset):
encryptedLetterOffset = (ord(originalLetter) - ord('a') + offset) % LETTERS_IN_ALPHABET
# deal with the case of decrypting using vigenere's cipher when letters must wrap around to from
# beginning to end of alphabet
if encryptedLetterOffset < 0:
return chr(ord('z') + encryptedLetterOffset)
return chr(ord('a') + encryptedLetterOffset)


# fills dictionaries with letter mappings for caesar's cipher
def constructLetterMappingsForCaesar():
for offset in range(LETTERS_IN_ALPHABET):
key = chr(ord('a') + offset)
val = computeNewLetter(key, 3)
letterMappings[key] = val
reverseLetterMappings[val] = key

# encrypts plaintext and returns result in all uppercase
def encrypt_caesar(plaintext):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Any reason why the maps are lowercase and the return values are uppercase?

encrypted = ''
for ch in plaintext.lower():
encrypted += letterMappings[ch]
return encrypted.upper()

# decrypts ciphertext and returns result in all uppercase
def decrypt_caesar(ciphertext):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way we can write these encrypt and decrypt methods using list comprehensions of some of the tools of functional programming? (e.g. map)

decrypted = ''
for ch in ciphertext.lower():
decrypted += reverseLetterMappings[ch]
return decrypted.upper()


# encrypts plaintext using a Vigenere cipher with a keyword.
def encrypt_vigenere(plaintext, keyword):
encrypted = ''
# convert keyword to lowercase string without spaces
keyword = keyword.lower().replace(" ", "")
for (index, ch) in enumerate(plaintext.lower()):
indexInKeyword = index % len(keyword)
offset = ord(keyword[indexInKeyword]) - ord('a')
encrypted += computeNewLetter(ch, offset)
return encrypted.upper()

# decrypts plaintext using a Vigenere cipher with a keyword.
def decrypt_vigenere(ciphertext, keyword):
decrypted = ''
# convert keyword to lowercase string without spaces
keyword = keyword.lower().replace(" ", "")
for (index, ch) in enumerate(ciphertext.lower()):
indexInKeyword = index % len(keyword)
offset = ord('a') - ord(keyword[indexInKeyword])
decrypted += computeNewLetter(ch, offset)
return decrypted.upper()

# encrypts plaintext using a railfence cipher.
def encrypt_railfence(plaintext, num_rails):
# handle edge cases
if num_rails == 0 or num_rails == 1:
return plaintext

encrypted = ''
# construct a list of step_sizes in which the index in the
# list corresponds to the step_size for that row. For the last row,
# step_size should loop around to the 0th index
step_sizes = [x for x in range( (2 * num_rails) - 2, 0, -2)]
text_length = len(plaintext)
for rail_num in range(num_rails):
step_size = step_sizes[rail_num % (num_rails - 1)]
encrypted += plaintext[rail_num : text_length : step_size]
return encrypted.upper()



""" this doesn't work at all.. I spent way too much time failing. I think all of the
other functions work """
# encrypts plaintext using a railfence cipher.
def decrypt_railfence(ciphertext, num_rails):
# handle edge cases
if num_rails == 0 or num_rails == 1:
return ciphertext

text_length = len(ciphertext)
# create a list that contains each rail
step_sizes = [x for x in range( (2 * num_rails) - 2, 0, -2)]
rails = []
start = 0
for rail_num in range(num_rails):
stop = int(text_length / num_rails) + 1 + start
rail = ciphertext[start : stop]
print(rail)
rails.append(rail)
start += len(rail)

rails = ['wecrlte', 'erdsoeefeaoc', 'aivden']
print(rails)


decrypted = ''
next_index_to_read = [0 for _ in range(num_rails)]
curr_rail = 0
for x in range(text_length):
print(decrypted)
print(curr_rail)
print(next_index_to_read[curr_rail])
index = next_index_to_read[curr_rail]
decrypted += rails[curr_rail][index]
next_index_to_read[curr_rail] += 1
if curr_rail == num_rails - 1:
offset = -1
elif curr_rail == 0:
offset = 1
curr_rail = (curr_rail + offset) % num_rails

return decrypted.upper()

# returns string indicating whether user wants to encrypt, decrypt, or quit
def promptForTask():
task = 'z'
while task not in ['e', 'd', 'q']:
task = input("Would you like to (e)ncrypt, (d)ecrypt, or (q)uit? ")[0].lower()

if task == 'e':
return 'encrypt'
if task == 'd':
return 'decrypt'
if task == 'q':
return 'quit'

# returns string indicating whether user wants to use caesar's, vigenere's, or railfence
def promptForCipherToUse():
choice = 'z'
while choice not in ['c', 'v', 'r']:
choice = input("Would you like to use (c)aesar's, (v)igenere's, or (r)ailfence? ")[0].lower()

if choice == 'c':
return 'caesar'
if choice == 'v':
return 'vigenere'
if choice == 'r':
return 'railfence'

# if user wants to use vigenere's this method returns a string to use as keyword.
# if user wants to use railfence, this method returns an int to use as num_rails
def promptForAdditionalInfo(cipherToUse):
if cipherToUse == 'vigenere':
return input('Provide a key: ')
return int(input('How many rails? '))

# returns text user wants to encrypt or decrypt
def promptForTextToUse():
return input('Enter a string to encrypt/decrypt: ')

# returns copy of text without punctuation characters
def removePunctuationAndSpace(text):
punctuation_set = set(string.punctuation)
whitespace_set = set(string.whitespace)
text = ''.join(ch for ch in text if ch not in punctuation_set and ch not in whitespace_set)
return text.replace(" ", "")

def readingFromFile():
response = input("Read from file [Y/N]? ")
if response[0].lower() == 'y':
return True
return False

def writingToFile():
response = input("Write to file [Y/N]? ")
if response[0].lower() == 'y':
return True
return False


def openFile():
filename = input("Enter a filename: ")
return open(filename, 'r+')

def performEncryption(originalText, cipherToUse, additional, writeToFile, fw):
if cipherToUse == 'caesar':
output = encrypt_caesar(originalText)
elif cipherToUse == 'vigenere':
output = encrypt_vigenere(originalText, additional)
else:
output = encrypt_railfence(originalText, additional)

# display result to user
if writeToFile:
fw.print('Transformed text: ' + output)
else:
print('Transformed text: ' + output)

def performDecryption(originalText, cipherToUse, additional, writeToFile, fw):
if cipherToUse == 'caesar':
output = decrypt_caesar(originalText)
elif cipherToUse == 'vigenere':
output = decrypt_vigenere(originalText, additional)
else:
output = decrypt_railfence(originalText, additional)

# display result to user
if writeToFile:
fw.write('Transformed text: ' + output)
else:
print('Transformed text: ' + output)

def run_suite():
"""
Runs a single iteration of the cryptography suite.

Asks the user for input text from a string or file, whether to encrypt
or decrypt, what tool to use, and where to show the output.
"""
constructLetterMappingsForCaesar()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we're constructing the letter mappings every time we run through this function.

# obtain task from user
task = promptForTask();
if task == 'quit':
return

readFromFile = False
if readingFromFile():
f = openFile()
readFromFile = True
else:
originalText = promptForTextToUse()
originalText = removePunctuationAndSpace(originalText)

writeToFile = False
fw = None
if writingToFile():
fw = openFile()
writeToFile = True


# obtain method from user
cipherToUse = promptForCipherToUse()
additional = 0
if cipherToUse != 'caesar':
additional = promptForAdditionalInfo(cipherToUse)

# perform operation
if task == 'encrypt':
if readFromFile:
for line in f:
performEncryption(removePunctuationAndSpace(line), cipherToUse, additional, writeToFile, fw)
else:
performEncryption(originalText, cipherToUse, additional, writeToFile, fw)
else:
if readFromFile:
for line in f:
performDecryption(removePunctuationAndSpace(line), cipherToUse, additional, writeToFile, fw)
else:
performDecryption(originalText, cipherToUse, additional, writeToFile, fw)


# Do not modify code beneath this point.
def should_continue():
"""
Asks the user whether they would like to continue.
Responses that begin with a `Y` return True. (case-insensitively)
Responses that begin with a `N` return False. (case-insensitively)
All other responses (including '') cause a reprompt.
"""
choice = input("Again (Y/N)? ").upper()
while not choice or choice[0] not in ['Y', 'N']:
choice = input("Please enter either 'Y' or 'N'. Again (Y/N)? ").upper()
return choice[0] == 'Y'


def main():
"""Harness for the Cryptography Suite"""
print("Welcome to the Cryptography Suite!")
run_suite()
while should_continue():
run_suite()
print("Goodbye!")


if __name__ == '__main__':
"""This block is run if and only if the Python script is invoked from the
command line."""
main()
23 changes: 23 additions & 0 deletions csmith95-assign1/feedback.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Name: Conner Smith
1) How long did this assignment take you to complete?
… I didn’t really have time to complete railfence decryption, but I spent about 2 hours on the parts before railfence and 4 hours thinking about railfence.


2) What has been the best part of the class so far?
I like the in-class lab days (the only days 2 days I’ve been in class were lab days). Doing the problems with a partner is more fun.




3) What can we do to make this class more enjoyable for you?
I’m not sure because I’ve only been to 2 classes, but I like the relaxed feel of the class. It feels very low stress, which is rare for a CS class.




4) What types of assignments would excite you in this class?

I liked this encryption assignment. A web-based assignment would be cool.



1 change: 1 addition & 0 deletions csmith95-assign1/railfence-cipher.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WECRLTEERDSOEEFEAOCAIVDEN
1 change: 1 addition & 0 deletions csmith95-assign1/railfence-plain.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WE ARE DISCOVERED. FLEE AT ONCE
1 change: 1 addition & 0 deletions csmith95-assign1/secret_message.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
KHOORZRUOG
1 change: 1 addition & 0 deletions csmith95-assign1/vigenere-cipher.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
LXFOPVEFRNHR
1 change: 1 addition & 0 deletions csmith95-assign1/vigenere-plain.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Attack At Dawn!