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
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.