CIT 591 Assignment 3: Playfair Cipher
Fall 2009, David Matuszek

Purposes of this assignment:

General idea of the assignment:

You are probably familiar with the idea of "secret codes," such as the cryptograms published in some newspapers. A cipher is a more secure way of making secret messages. In this assignment you will write a program to encipher and decipher secret messages according to the Playfair cipher. (For more about the Playfair cipher, see the Wikipedia article.)

The general algorithms

Here's how to construct a Playfair cipher.

  1. Choose a keyword or key phrase, for example, university.
  2. Discard all non-letters (whitespace, punctuation, digits), and lowercase any captial letters.
  3. If the letter j occurs in the keyword, change it to i. (So university is not changed.)
  4. If a letter occurs more than once in the keyword, keep only the first occurrence. (So university becomes universty.)
  5. Put the letters of the keyword into a 5 by 5 array (implement this as a list of lists).
    The array you are working with The list representation of the array
    u n i v e
    r s t y  
             
             
             
    [['u', 'n', 'i', 'v', 'e'],
     ['r', 's', 't', 'y', ' '],
     [' ', ' ', ' ', ' ', ' '],
     [' ', ' ', ' ', ' ', ' '],
     [' ', ' ', ' ', ' ', ' ']]
  6. Fill in the rest of the letters of the alphabet, in order, omitting j.
    The array you are working with The list representation of the array
    u n i v e
    r s t y a
    b c d f g
    h k l m o
    p q w x z
    [['u', 'n', 'i', 'v', 'e'],
     ['r', 's', 't', 'y', 'a'],
     ['b', 'c', 'd', 'f', 'g'],
     ['h', 'k', 'l', 'm', 'o'],
     ['p', 'q', 'w', 'x', 'z']]

Here's how to encode a message, using the array:

  1. Make all the letters lowercase, and throw away all blanks and punctuation marks.
  2. Change any letter j to i.
  3. Take the next two letters of the message. We can't let them be both the same, so:
    1. If only one letter remains, use x as the second letter.
    2. If the two letters are the same, use x instead of the second letter. (The second letter will be the first letter of the next group of two.)
    3. If the two letters are both x, skip one of them (and use the next letter along as the second letter).
  4. If the two letters are in the same column, encode each one with the letter below it, wrapping around if necessarly. (That is, if the letter is in the bottom row, move to the top row.) For example, id become tl, while up becomes ru.
  5. If the two letters are in the same row, encode each one with the letter to its right, wrapping around if necessarly. (That is, if the letter is in the rightmost column, move to the leftmost column.) For example, st become ty, while lo becomes mh.
  6. If the two letters are in different rows and different columns, they form a rectangle. Replace each letter with the letter in the same row but in the other corner of the rectangle. For example, sm becomes yk.
  7. Print out the result as a string of letters, with no blanks or other punctuation.

Example:

  Programming in Python is fun!  # message to be enciphered
  programminginpythonisfun       # lowercased, no j's, no non-letters
  pr og ra mx mi ng in py th on is fu nx   # letters two at a time
  ub zo sr xv lv ec vi xr rl ke nt bv vq   # encoded two at a time
  ubzosrxvlvecvixrrlkentbvvq     # enciphered message

Here's how to decode a message, using the array:

Example:

  ubzosrxvlvecvixrrlkentbvvq     # enciphered message
  ub zo sr xv lv ec vi xr rl ke nt bv vq 
  pr og ra mx mi ng in py th on is fu nx
  programxminginpythonisfunx     # the best we can do at deciphering it

This is as far as you can get in decoding the message. The message is readable, with a little effort on the human's part.

Your assignment:

Using Test-Driven Design, create a program named playfair.py. It should have at least these two methods:

def encode(plainTextMessage, secretPhrase)
Encodes a plainTextMessage, using the secretPhrase. Both the message and the secret phrase may contain capital letters, the letter J, and non-alphabetic characters.
def decode(encodedMessage, secretPhrase)
Decodes an encodedMessage, using the secretPhrase. The secret phrase may contain capital letters, the letter J, and non-alphabetic characters, but the encoded message will consist of an even number of letters only, with each pair of letters ready to be decoded.
 

Every method you write should have a unit test. I have two "top level" tests for you; these will be the last tests you will be able to pass, since they depend on everything else working perfectly.

import playfair
import unittest

class TestPlayfair(unittest.TestCase):

    def testEncode(self):
        message1 = 'Test message ONE.'
        message2 = 'Boo, Exxon Oil!'
        
        secretPhrase = 'abc'
        self.assertEquals('udtupbxcqckbpocz',
                          playfair.encode(message1, secretPhrase))
        self.assertEquals('dmpdynopfo',
                          playfair.encode(message2, secretPhrase))

        secretPhrase = "Amazingly Few Discotheques Provide Jukeboxes!"
        self.assertEquals('pgwqnlokwzlgpeon',
                          playfair.encode(message1, secretPhrase))
        self.assertEquals('xcponpepmf',
                          playfair.encode(message2, secretPhrase))

    def testDecode(self):
        message1 = 'Test message ONE.'
        message2 = 'Boo, Exxon Oil!'

        secretPhrase = 'abc'
        self.assertEquals('testmesxsageonex',
                          playfair.decode('udtupbxcqckbpocz', secretPhrase))
        self.assertEquals('booexonoil',
                          playfair.decode('dmpdynopfo', secretPhrase))

        secretPhrase = "Amazingly Few Discotheques Provide Jukeboxes!"
        self.assertEquals('testmesxsageonex',
                          playfair.decode('pgwqnlokwzlgpeon', secretPhrase))
        self.assertEquals('booexonoil',
                          playfair.decode('xcponpepmf', secretPhrase))

unittest.main()

Due date:

Before 6 AM, Friday October 2, 2009, via Blackboard.