-
Notifications
You must be signed in to change notification settings - Fork 0
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
SBWKRQ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
PYTHON |
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 | ||
""" | ||
import string | ||
|
||
LETTERS_IN_ALPHABET = 26 | ||
|
||
# a dictionary of char-to-char mappings for | ||
# Caesar's cipher | ||
letterMappings = {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() |
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. | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
WECRLTEERDSOEEFEAOCAIVDEN |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
WE ARE DISCOVERED. FLEE AT ONCE |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
KHOORZRUOG |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
LXFOPVEFRNHR |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Attack At Dawn! |
There was a problem hiding this comment.
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 :)