CS 212 — Homework One

Solitaire Cipher

Due: 05 February 2013, 11:59p

Worth: 80 points

Note: I forgot to mention below, but please but all classes in a cs211.hw1 package and all tests in cs211.hw1.test.

Part one: Finishing the implementation

All of the code that we have been working with in class has been posted to the homepage. Because we were more interested in specification than implementation, we did not fill in all of the methods in ArrayList. Your first task is to complete the implementation of the list.

Before you begin, however, you will need to revisit the JUnit tests. We didn't finish implementing all of the tests, so before you start fleshing out the ArrayList, you will need to finish writing the tests. Once you have done that, implement the other methods so that the tests pass.

Part two: Building an encryption/decryption engine

Background

We will be implementing a kind of encryption that is sometimes called key stream encryption. The idea is that we have a stream of "key" values that we use to modify the letters in our original message. To encrypt a message, we visit each character in the message in turn and add the corresponding key value to it, using modulo arithmetic to make sure that the result is still a valid character. Decryption is the reverse of this. We subtract the corresponding key value from the encrypted character using modulo arithmetic to return to the original character. It is, of course, very important that the key value we use to encrypt and decrypt the character are the same.

The simplest key stream we could use would always provide exactly the same key. You may have encountered rot13, which is used fairly heavily online for weakly obscuring forum posts (for example, to avoid spoilers). This just uses the number 13 for all key values, so the whole message just rotates forward 13 characters in the alphabet. The advantage of using 13, is that if you encrypt something twice, you get the original back. Of course, the downside of single valued key streams is that they are very easy to crack. Since we are doing modulo arithmetic, there are at most 26 different rotations, so checking them all is trivial. What we want is a key stream that changes values for every character unpredictably, while at the same time still being something that the recipient of our message can also generate so that the message can actually be decrypted.

A classic example would be things like one time use code books, which are just lists of keys that the sender and the recipient both have. The Enigma machines, which you will know something about if you have ever looked into the history of CS, are another classic example of a key stream generator. The operators would dial in some initial setting based on some other short communication or based on the day, and then the machine would mechanically generate a complex series of key values based on those initial dial settings.

We are going to be looking at something called the Solitaire Encryption Algorithm, which was developed by security exert Bruce Schneier for Neal Stephenson's novel "Cryptonomicon", which maybe some of you have read. In essence, it is an algorithm for generating a key stream using a deck of cards. The only requirement is that both you and the person you are communicating with both start with a deck of cards that has been shuffled identically. There is then a fairly complex process of cutting the deck and walking through the cards to generate each new key.

In the interest of full disclosure, this really is meant to be a manual process that uses a real physical deck of cards. There is really no reason to write a computer program that follows this particular approach... unless you are taking a class where you are told to do so. Okay, the other reason to do it is to understand the algorithm. Since the computer is so dumb, writing programs is a great way to understand processes. Anyway, this is an academic exercise, not how we would ever implement a key stream generator for real digital encryption (though the principle is the same).

Implementation:CipherEngine

While our target is to perform Solitaire encryption, we are going to build a fairly general framework to support it. The first class we are going to build is a CipherEngine. This is a completely generic class for handling key stream encryption. The basic idea is that we will supply an Iterator that returns the next key in the sequence on every call to next(). This will allow us to implement any key stream encryption just by swapping out the iterator. The CipherEngine class should have two public methods:

public static String encrypt(String message, Iterator<Integera> keyStream)
This will take in a message and encrypt it use the sequence of keys generated by the iterator.
public static String decrypt(String message, Iterator<Integer> keyStream)
This performs the compliment of the encrypt method, returning the original message (sort of — see below).

As mentioned above, the Solitaire encryption is meant to be a manual process (as is rot13, for that matter). To keep things simple, the algorithm is designed to work on an input alphabet of just 26 characters. That means that we will throw away case, punctuation and whitespace. Once we throw it away, it is gone for good, so this is not a fully symmetric process; decrypting an encrypted message will not return the original message. So, the first thing that encrypt will do to the message will be to convert it to all upper case letters and break it into five character blocks (this is just a conventional form). So, for example, the message "Someone is watching" will become "SOMEO NEISW ATCHI NG" (which almost looks like it is already encrypted). You don't actually have to do this to the whole message before you start, you can process each character as you see it, capitalizing each character, throwing it out if it isn't a letter, and inserting an extra space every sixth character.

The encryption process then follows these steps:

I recommend using a StringBuilder to store the individual characters as they are generated, and then using the toString method to return the final String.

The decryption process is essentially the same. We don't have to worry about converting to upper case or the five character blocks since the only valid messages will look that way already (but handle bad data gracefully). Instead of adding the key value, we will subtract it. This may cause the value to become negative. If it does, add 26 to the result (the mod operator won't help here since Java treats it as a remainder operator and thus allows negative remainders). Other than that, the process is the same. The result will look like the five character blocks shown above.

Implementation: Rot13Iterator

To test this with an easily predictable key stream, we will start by implementing a rot13 iterator. This is very simple. It should implement an Iterator<Integer>, which will require it to have the following three methods:

public boolean hasNext()
This will always return true — there is always a next key.
public Integer next()
This always returns 13. As I said, rot13 is easy, we just add 13 to everything. This is not how I would implement rot13 on its own, but it makes sense in the context of this assignment
public remove()
This should throw an UnsupportedOperationException. This is an optional method on Iterators, which means that it doesn't have to actually remove anything, it just has to be callable.

That is it. Pretty simple, huh? Write some tests that use it to make sure your CipherEngine works well before throwing the more complex cipher at it.

Implementation: CardDeck

Before we can write this class, we need one other class, something to represent a deck of cards. We could represent each card properly, but for the algorithm, we will be assigning numeric values to each card, so we will just skip a step and represent each card as a single Integer. We will assign the values so that the first suit maps the cards Ace to King as 1-13, the second suit will be 14-26, etc... The ordering of the suits matters if we are dealing with a real deck of cards, but since we jumped right to integers, we don't actually care. The algorithm requires that we have two jokers that are distinctly different, and we will map these to 53 and 54. So, the underlying storage of the deck will be an ArrayList of Integers. We will implement this in a class called CardDeck with the following methods:

public CardDeck()
The constructor should create the actual representation of the deck of cards. This will be in the form of an ArrayList of Integers.
public void shuffle(long seed)
This will shuffle the deck. The seed will be used to initialize the Random object that we use to randomize the card placements. Usually, we are content with the default (the current time in milliseconds), but we want to be able to create decks with identical shuffles.
public Integer getCard(int index)
Get the value of the card stored at index
public int indexOf(Integer card)
This finds a particular card in the deck and returns its index.
public void swap(int index1, int index2)
This swaps the location of two cards in the deck.
public void swap(int start1, int end1,int start2,int end2)
This is a more complicated swap, but one that will be useful for the algorithm. This swaps two "chunks" of cards. It takes all of the cards starting at index start1 up to, but not including end1 and swaps their location with the location of all of the cards from start2 up to, but not including end2. If end1 is equal to start2 this would cut the deck — take the top half of the deck and put it under the bottom half. Watch out for all of the invalid cases here.

Only two of these are at all tricky. The first is the range based swap. To illustrate how tricky, consider this case (the two ranges are colored): 1 2 3 4 5 6 7 8 9 10 11 12 13. After the swap it will look like: 1 8 9 6 7 2 3 4 5 10 11 12 13. My recommendation is to think about this as five different (possibly empty) ranges: the numbers before the first range, the first range, the numbers between the two ranges, the second range, and finally, the numbers after the second range. I also suggest using a second ArrayList rather than trying to figure out how to do this in place. You should also note that [start1-end1] is not necessarily the first range...

The other tricky method is the shuffle. I recommend using the something like the Fisher-Yates shuffle. Basically, you view the array as being divided into two chunks, the shuffled portion (at the end) and the unshuffled portion (at the beginning). You pick a random index from the unshuffled portion, swap it with the value you find at the end of the unshuffled portion and then move the boundary between the two by one slot. Visit the link for more details.

Implementation: SolitaireIterator

The iterator itself is now pretty straightforward. Here are the methods:

public SolitaireIterator(long seed)
The should create a new deck of cards and then shuffle it using the supplied seed.
public boolean hasNext()
This will always return true — there is always a next key.
public Integer next()
This will return the appropriate next integer key in the sequence.
public remove()
This should throw an UnsupportedOperationException.

The only tricky thing here is next(). This is where you will actually implement the Solitaire algorithm, which is a sequence of deck manipulations to arrive at a card that is used for the key. Rather than trying to expound on it here, I will point you at Bruce Schneier's actual instructions. You can read the whole thing, but we covered the first half above. The next() method will just implement the part down in Generating the Keystream Letters. You can also consult Wikipedia's description for a slightly terser break down of the steps required.

Grading

PointsItemComments
10 ptsArrayList
15 ptsCipherEngine
5 ptsRot13Iterator
20 ptsCardDeck
20 ptsSolitaireIterator
10 ptsStyle, approach, organization, etcInstructor points

This semester I am experimenting with using Web-CAT to help manage the grading. With the exception of the last entry, you will get instant feedback on how successful you were at implementing the various classes. The points for the individual classes are calculated by multiplying the percentage of your own tests that you pass by the percentage of your code that is covered by your tests and then by the percentage of my tests that you pass. You will allowed multiple submissions so you can improve your score based on the feedback.

Turning the assignment in...

All work will be turned in via Web-CAT. Read the setup guide.


Last modified: Sun Feb 3 01:40:38 EST 2013