Skip to content

added submission for lglucin #7

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 lglucin-assign1/caesar-cipher.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SBWKRQ
1 change: 1 addition & 0 deletions lglucin-assign1/caesar-plain.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PYTHON
318 changes: 318 additions & 0 deletions lglucin-assign1/crypto.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
"""
Assignment 1: Cryptography
Course: CS 92SI
Name: Lloyd Lucin
Date: 10/9/2015

This console program allows users to perform string encryption
and decryptions.
Encryption styles are: Caesar, Vigenere, and
Railfence.
Users may either type a string to encrypt/decrypt or provide
a text file.
"""

import string


def caesarEncryptMap():
"""
Returns a dictionary where keys are capital letters
of the alphabet and values are letters that are
'three away' from the key. Wraps around for letters
X, Y, and Z.
"""
plainToEncryptedChars = {}
for c in string.ascii_uppercase:
if ord(c) >= ord('X'):
refC = chr(ord(c) - len(string.ascii_uppercase))
else:
refC = c
plainToEncryptedChars[c] = chr(ord(refC) + 3)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

One pythonic way to do this is through a dictionary comprehension and some fancy list splicing :)

letters = string.ascii_uppercase
plainToEncryptedChars = dict(zip(letters, letters[3:] + letters[:3]))

return plainToEncryptedChars


def encrypt_caesar(plaintext):
"""
Encrypts plaintext using a Caesar cipher.
We create a dictionary that returns a dictionary
of alphabets to their letter '3 spots ahead',
which we use to build our encrypted string.
"""

plainToEncryptedChars = caesarEncryptMap()

encrypted = ""
for c in plaintext:
encrypted += plainToEncryptedChars[c.upper()]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This would be a good candidate for a list comprehension or map!

return encrypted


def decrypt_caesar(ciphertext):
"""
Decrypts a ciphertext using a Caesar cipher.
We take the dictionary from caesarEncryptMap() and
reverse the key value pairs. We then use this
dictionary to decrypt our ciphertext.
"""
plainToEncryptedChars = caesarEncryptMap()
encryptedToPlainChars = dict(zip(plainToEncryptedChars.values(), plainToEncryptedChars.keys()))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is well done and very pythonic!


decrypted = ""
for c in ciphertext:
decrypted += encryptedToPlainChars[c.upper()]
return decrypted



def encrypt_vigenere(plaintext, keyword):
"""
Encrypts plaintext using a Vigenere cipher with a keyword.
We encrypt each character through modular operations with the
ASCII representation of each character.
"""
encrypted = ""
plaintext = plaintext.upper()
keyword = keyword.upper()

for i in range(len(plaintext)):
alphaIndex = (ord(keyword[i%(len(keyword))]) + ord(plaintext[i]) - 2 * ord('A')) % 26
encrypted += chr(alphaIndex + ord('A'))
return encrypted


def decrypt_vigenere(ciphertext, keyword):
"""
Decrypts ciphertext using a Vigenere cipher with a keyword.
We decrypt each character through modular operations with the
ASCII representation of each character.
"""
decrypted = ""
ciphertext = ciphertext.upper()
keyword = keyword.upper()

for i in range(len(ciphertext)):
alphaIndex = (ord(ciphertext[i]) - ord(keyword[i%(len(keyword))])) % 26
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well done here, only nit is to factor out the 26 as a constant

decrypted += chr(alphaIndex + ord('A'))
return decrypted


def dotMatrix(num_rails, width):
"""
Returns a num_railsXwidth matrix of all '.'
"""
matrix = []
for i in range(num_rails):
l = ['.' for i in range(width)]
matrix.append(l)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A common pythonic way to do this is to use nested list comprehensions:
matrix = [['.' for _ in range(width)] for _ in range(num_rails)]

Notice the use of _ for unused counter variables

return matrix


def encrypt_railfence(plaintext, num_rails):
"""
Encrypts plaintext using a railfence cipher.
We create a matrix of all '.' of size plaintextXnum_rails.
We then insert characters of the plaintext into this
matrix in railfence format. Afterwards we build our
encryption by reading each non '.' character across each
row of our matrix.
"""

plaintext = plaintext.upper()

# Special Case
if num_rails == 1:
return plaintext

# Create matrix of '.'s
matrix = dotMatrix(num_rails, len(plaintext))
leftDown = True
r = 0
c = 0

# Insert letters into rails
for i in range(len(plaintext)):
matrix[r][c] = plaintext[i]
if leftDown:
r += 1
else:
r -= 1
if r == 0 or r == (num_rails - 1):
leftDown = not leftDown
c += 1
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pretty clean code here - well done!


# Encrypt from Rails
encrypted = ""
for l in matrix:
for c in l:
if c != '.':
encrypted += c;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good candidate for a list comprehension here and possibly the filter utility for !='.'


for line in matrix:
print(line)
return encrypted


def decrypt_railfence(ciphertext, num_rails):
"""
Encrypts plaintext using a railfence cipher.
Constructs the same railfence matrix from encrypt_railfence().
It then, reads diagonally down and up moving right to decrypt
the message.
"""

ciphertext = ciphertext.upper()

# Special Case
if num_rails == 1:
return ciphertext
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice catch


# Create Railfence matrix
matrix = dotMatrix(num_rails, len(ciphertext))
cipherIndex = 0
step = 2 * num_rails - 2

# For every list
for i in range(len(matrix)):
row = matrix[i]
for j in range(len(row)):
move = i + j * step
if i != 0 and i != (num_rails - 1):
move = i + j * step//2
# For all odd values
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm under the impression that looping over odd values in the way that you do can be simplified by doing for j in range(len(row))[1::2]

if j%2 != 0 and move < len(row):
if i < num_rails//2:
# Move right until you find a letter to the right and up in the matrix
while move < len(ciphertext) - 2 and matrix[i - 1][move + 1] == '.':
move += 1
if i >= num_rails//2:
if i != num_rails//2 or num_rails%2 == 0:
# Move left until you find a letter to the right and up in the matrix
while move > 1 and matrix[i - 1][move + 1] == '.':
move -= 1
# Insert the character of ciphertext into matrix
if move < len(row):
print("Assigning: " + str(i) + ", " +str(move))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Remove debugging prints like this

matrix[i][move] = ciphertext[cipherIndex]
cipherIndex += 1

# Read Diagonally to decrypt
decrypted = ""
leftDown = True
r = 0
c = 0
for i in range(len(ciphertext)):
decrypted += matrix[r][c]
if leftDown:
r += 1
else:
r -= 1
if r == 0 or r == (num_rails - 1):
leftDown = not leftDown
c += 1

for line in matrix:
print(line)
return decrypted


def read_from_file(filename):
"""
Reads and returns content from a file.
"""
with open(filename, 'r') as f:
first_line = f.readline()
return first_line


def write_to_file(filename, content):
"""
Writes content to a file.
"""
target = open(filename, 'w')
target.write(content)


def keep_prompting(prompt, *validAnswers):
"""
Keeps prompting the user until they provide
a satisfactory answer.
"""
answer = input(prompt)
while answer.upper() not in validAnswers:
print("Please respond with: ", end="")
print(''.join('\"{},\" '.format(*k) for k in validAnswers))
answer = input(prompt)
return answer.upper()

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.
"""
print("Welcome to Cryptography Suite!")
print("*Input*")
fileOrString = keep_prompting("(F)ile or (S)tring? ", 'F', 'S')
if fileOrString == 'S':
usrInput = input("Enter the string to encrypt: ")
usrInput = ''.join(ch for ch in usrInput if ch.isalnum())
else:
usrInput = read_from_file(input("Filename: "))
print("*Transform*")
encrypt = keep_prompting("(E)ncrypt or (D)ecrypt? ", 'E', 'D') == 'E'
encryptionStyle = keep_prompting("(C)aesar, (V)igenere, or (R)ailfence? ", "C", "V", "R")
if encryptionStyle == "C":
if encrypt:
output = encrypt_caesar(usrInput)
else:
output = decrypt_caesar(usrInput)
if encryptionStyle == "V":
keyword = input("Passkey? ")
if encrypt:
output = encrypt_vigenere(usrInput, keyword)
else:
output = decrypt_vigenere(usrInput, keyword)
if encryptionStyle == "R":
num_rails = int(input("Number of rails? "))
if encrypt:
output = encrypt_railfence(usrInput, num_rails)
else:
output = decrypt_railfence(usrInput, num_rails)

print("*Output*")
writeFile = keep_prompting("(F)ile or (S)tring? ", 'F', 'S') == 'F'
if writeFile:
write_to_file(input("Filename: "), output)
else:
print(output)


# 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 lglucin-assign1/feedback.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Name: <YOUR NAME>
1) How long did this assignment take you to complete?



2) What has been the best part of the class so far?





3) What can we do to make this class more enjoyable for you?





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


Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please help us out with feedback in the future!




1 change: 1 addition & 0 deletions lglucin-assign1/outputrailfence.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WEAREDISCOVEREDFLEEATONCE
1 change: 1 addition & 0 deletions lglucin-assign1/railfence-cipher.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WECRLTEERDSOEEFEAOCAIVDEN
1 change: 1 addition & 0 deletions lglucin-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 lglucin-assign1/secret_message.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
KHOORZRUOG
1 change: 1 addition & 0 deletions lglucin-assign1/vigenere-cipher.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
LXFOPVEFRNHR
1 change: 1 addition & 0 deletions lglucin-assign1/vigenere-plain.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Attack At Dawn!