Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Write a Python program plaifair.py as follows: def playfair(key, plaintext): # y

ID: 3600213 • Letter: W

Question

Write a Python program plaifair.py as follows:

def playfair(key, plaintext):

# your code here

if __name__ == "__main__":

     # your code here

The conditional statement above allows the program to be ran directly.

STEP A - Compute and print the encryption table

1 - Create a 5x5 table as a list of lists.

2 – Define a function called init_table() which initializes the table with stars. This function does not return anything.

3 – Print out the table. The output should look like this:

* * * * *

* * * * *

* * * * *

* * * * *

* * * * *

4 – Define a function called table_has(letter) which checks if a letter already exists in the table. This function should return true or false depending on whether the letter exists.

5 – Define a function clean_key(key) which changes the secret key to uppercase and replace I by J, then returns the clean key.

6 – Define a function set_cell(letter) which sets a table cell to a specific letter.

7 – Define a function create_table(key) to populate the encryption table given the secret key. This function should:

Initialize the table.

Clean the secret key.

Build the table using the set_cell() and table_has() functions.

8 – Print out the encryption table.

STEP B – Encrypt your secret message

1 – Define a function find_letter(letter) which returns the row and column of the letter in the table.

2 - Define a function encode_pair(a, b) which takes a pair of letters a and b, runs the encoding rules and returns the encoded pair as follows:

“AB” -> “LF” (sample)

Function should print an error message if a equals b and stop program execution.

Suggested approach:

Compute a_row, a_col, b_row, b_col using the find_letter function.

Use an if-elif-else statement to compute a_new_row, a_new_col, b_new_row, b_new_col based on the encryption rules.

Optional: To make your code cleaner, you may want to create separate encoding functions for the 3 different cases: same row (with wrapping), same column (with wrapping), and rectangle.

3 – Define a function cleanup(plaintext) which returns the cleaned up message as follows:

Changes it to uppercase.

Removes spaces.

Replaces J by I.

Places a letter “X” between 2 consecutive repeating letters as follows:

EE -> EXE

If the repeated letter happen to be an “X”, insert the letter Q in between as follows:

XX -> XQX

If the length of the resulting text is an add number, add the letter “Z” at the end as follows:

..G -> ..GZ

If the last letter happens to be a “Z”, add the letter “Q” at the end as follows:

..Z -> ..ZQ

Suggested approach:

Note that fixing a problem may lead to an new one. E.g. inserting an “X” may lead to a plaintext with an odd length. Therefore, you should create a while loop which will repeatedly call a function to fix issues and return a new plaintext until no more issues are found.

STEP C – Encrypt your secret message

1 – Define a function encrypt(plaintext) which encodes the message plaintext and returns the ciphertext. Make sure to insert a space every 5 letters in the resulting ciphertext (e.g. KLDGD NXSLO CWGQH KUINF ACWER)

2 – Execute and print the ciphertext. The execution phase should:

a. Prompt for the secret key.

b. Prints the secret key as entered.

c. Prompt for the secret message (plaintext).

d. Prints the secret message as entered.

e. Computes and prints the encrypted message (ciphertext).

f. Asks if user wants to encrypt another message

If “yes”, program goes back to step a.

If “no”, program stops execution.

STEP D – Extra-Credit

Create a function decrypt(key, ciphertext) which takes two parameters, the secret and the ciphertext and returns the plaintext.

THE SUBMISSION

Submit a playfair.txt or py file as follows:

Program should be structure as described in this project.

All functions should have the same names as indicated above. They may be tested separately for partial credit in case your program fails.

All functions have a comment above them explaining their purpose.

Submission should be free from debugging code or test code.

TESTING YOUR CODE – Not to include with your submission

Create a Python program called test_playfair.py in and place it in the same directory as your main program. The code should import your encryption code and then run some tests as follows:

import playfair

def test(key, plain, expected):

cipher = playfair.playfair(key, plain)

if cipher != expected:

print("%s : %s != %s" % (plain, cipher, expected))

# execute test:

test(“mysecretkey”, “this is my secret message”, “WQIUW QOKSG ADAJH AOIQU WIQUW”)

Explanation / Answer

import re

class PlayfairError(Exception):

def __init__(self, message):

print message

class Playfair:

# omissionRule determines which omission rule you want to use (go figure). See the list at the beginning of the constructor

# doublePadding determines what letter you would like to use to pad a digraph that is double letters

# endPadding determines what letter you would like to use to pad the end of an input containing an odd number of letters

def __init__(self, omissionRule = 0, doublePadding = 'X', endPadding = 'X'):

omissionRules = [

'Merge J into I',

'Omit Q',

'Merge I into J',

]

if omissionRule >= 0 and omissionRule < len(omissionRules):

self.omissionRule = omissionRule

else:

raise PlayfairError('Possible omission rule values are between 0 and ' + (len(omissionRules) - 1) + '.')

# start with a blank password

self.grid = self.generateGrid('')

# make sure the input for the double padding character is valid

if len(doublePadding) != 1:

raise PlayfairError('The double padding must be a single character.')

elif not self.isAlphabet(doublePadding):

raise PlayfairError('The double padding must be a letter of the alphabet.')

elif doublePadding not in self.grid:

raise PlayfairError('The double padding character must not be omitted by the omission rule.')

else:

self.doublePadding = doublePadding.upper()

# make sure the input for the end padding character is valid

if len(endPadding) != 1:

raise PlayfairError('The end padding must be a single character.')

elif not self.isAlphabet(endPadding):

raise PlayfairError('The end padding must be a letter of the alphabet.')

elif endPadding not in self.grid:

raise PlayfairError('The end padding character must not be omitted by the omission rule.')

else:

self.endPadding = endPadding.upper()

# returns None if the letter should be discarded, else returns the converted letter

def convertLetter(self, letter):

if self.omissionRule == 0:

if letter == 'J':

letter = 'I'

return letter

elif self.omissionRule == 1:

if letter == 'Q':

letter = None

return letter

elif self.omissionRule == 2:

if letter == 'I':

letter = 'J'

return letter

else:

raise PlayfairError('The omission rule provided has not been configured properly.')

# returns the alphabet used by the cipher (takes into account the omission rule)

def getAlphabet(self):

fullAlphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

alphabet = ''

for letter in fullAlphabet:

letter = self.convertLetter(letter)

if letter is not None and letter not in alphabet:

alphabet += letter

return alphabet

# generates the 25 character grid based on the omission rule and the given password

def generateGrid(self, password):

grid = ''

alphabet = self.getAlphabet()

for letter in password:

if letter not in grid and letter in alphabet:

grid += letter

for letter in alphabet:

if letter not in grid:

grid += letter

return grid

# splits the text input into digraphs

def generateDigraphs(self, input):

input = self.toAlphabet(input).upper()

inputFixed = ''

for i in range(len(input)):

letter = self.convertLetter(input[i])

if letter is not None:

inputFixed += letter

digraphs = []

counter = 0

while counter < len(inputFixed):

digraph = ''

if counter + 1 == len(inputFixed): # we have reached the end of the inputFixed

digraph = inputFixed[counter] + self.endPadding

digraphs.append(digraph)

break

elif inputFixed[counter] != inputFixed[counter + 1]: # we just need to create a normal digraph

digraph = inputFixed[counter] + inputFixed[counter + 1]

digraphs.append(digraph)

counter += 2

else: # we have a double letter digraph, so we add the double padding

digraph = inputFixed[counter] + self.doublePadding

digraphs.append(digraph)

counter += 1

return digraphs

# encrypts a digraph using the defined grid

def encryptDigraph(self, input):

if len(input) != 2:

raise PlayfairError('The digraph that is going to be encrypted must be exactly 2 characters long.')

elif not self.isUpper(input):

raise PlayfairError('The digraph that is going to be encrypted must contain only uppercase letters of the alphabet.')

firstLetter = input[0]

secondLetter = input[1]

firstLetterPosition = self.grid.find(firstLetter)

secondLetterPosition = self.grid.find(secondLetter)

firstLetterCoordinates = (firstLetterPosition % 5, firstLetterPosition / 5)

secondLetterCoordinates = (secondLetterPosition % 5, secondLetterPosition / 5)

if firstLetterCoordinates[0] == secondLetterCoordinates[0]: # letters are in the same column

firstEncrypted = self.grid[(((firstLetterCoordinates[1] + 1) % 5) * 5) + firstLetterCoordinates[0]]

secondEncrypted = self.grid[(((secondLetterCoordinates[1] + 1) % 5) * 5) + secondLetterCoordinates[0]]

elif firstLetterCoordinates[1] == secondLetterCoordinates[1]: # letters are in the same row

firstEncrypted = self.grid[(firstLetterCoordinates[1] * 5) + ((firstLetterCoordinates[0] + 1) % 5)]

secondEncrypted = self.grid[(secondLetterCoordinates[1] * 5) + ((secondLetterCoordinates[0] + 1) % 5)]

else: # letters are not in the same row or column, i.e. they form a rectangle

firstEncrypted = self.grid[(firstLetterCoordinates[1] * 5) + secondLetterCoordinates[0]]

secondEncrypted = self.grid[(secondLetterCoordinates[1] * 5) + firstLetterCoordinates[0]]

return firstEncrypted+secondEncrypted

# decrypts a digraph using the defined grid

def decryptDigraph(self, input):

if len(input) != 2:

raise PlayfairError('The digraph that is going to be encrypted must be exactly 2 characters long.')

elif not self.isUpper(input):

raise PlayfairError('The digraph that is going to be encrypted must contain only uppercase letters of the alphabet.')

firstEncrypted = input[0]

secondEncrypted = input[1]

firstEncryptedPosition = self.grid.find(firstEncrypted)

secondEncryptedPosition = self.grid.find(secondEncrypted)

firstEncryptedCoordinates = (firstEncryptedPosition % 5, firstEncryptedPosition / 5)

secondEncryptedCoordinates = (secondEncryptedPosition % 5, secondEncryptedPosition / 5)

if firstEncryptedCoordinates[0] == secondEncryptedCoordinates[0]: # letters are in the same column

firstLetter = self.grid[(((firstEncryptedCoordinates[1] - 1) % 5) * 5) + firstEncryptedCoordinates[0]]

secondLetter = self.grid[(((secondEncryptedCoordinates[1] - 1) % 5) * 5) + secondEncryptedCoordinates[0]]

elif firstEncryptedCoordinates[1] == secondEncryptedCoordinates[1]: # letters are in the same row

firstLetter = self.grid[(firstEncryptedCoordinates[1] * 5) + ((firstEncryptedCoordinates[0] - 1) % 5)]

secondLetter = self.grid[(secondEncryptedCoordinates[1] * 5) + ((secondEncryptedCoordinates[0] - 1) % 5)]

else: # letters are not in the same row or column, i.e. they form a rectangle

firstLetter = self.grid[(firstEncryptedCoordinates[1] * 5) + secondEncryptedCoordinates[0]]

secondLetter = self.grid[(secondEncryptedCoordinates[1] * 5) + firstEncryptedCoordinates[0]]

return firstLetter+secondLetter

# encrypts text input

def encrypt(self, input):

digraphs = self.generateDigraphs(input)

encryptedDigraphs = []

for digraph in digraphs:

encryptedDigraphs.append(self.encryptDigraph(digraph))

return ''.join(encryptedDigraphs)

# decrypts text input

def decrypt(self, input):

digraphs = self.generateDigraphs(input)

decryptedDigraphs = []

for digraph in digraphs:

decryptedDigraphs.append(self.decryptDigraph(digraph))

return ''.join(decryptedDigraphs)

# sets the password for upcoming encryptions and decryptions

def setPassword(self, password):

password = self.toAlphabet(password).upper()

self.grid = self.generateGrid(password)

# strips out all non-alphabetical characters from the input

def toAlphabet(self, input):

return re.sub('[^A-Za-z]', '', input)

# tests whether the string only contains alphabetical characters

def isAlphabet(self, input):

if re.search('[^A-Za-z]', input):

return False

return True

# tests whether the string only contains uppercase alphabetical characters

def isUpper(self, input):

if re.search('[^A-Z]', input):

return False

return True

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote