
DUE by 10:00 PM on Friday, March 15, 2013 (extended to 10pm Friday, March 22nd, 2013)
NO LATE PROGRAMS WILL BE ACCEPTED!
Corrections, clarifications, and other announcements regarding this programming assignment will be found below.
Assignment Goals:
In this assignment, you will implement a simplified simulation of the German Enigma Machine, which was used by the German military to encrypt and decrypt messages during World War II.
The Enigma machine, invented by German engineer Arthur Scherbius, caused many headaches for Allied codebreakers during World War II. The Germans used the machine to encrypt and decrypt their top secret communications, and they were confident that the Allies would have a difficult time breaking the codes.
Let's begin with a basic overview of some cryptography terminology, as they apply to the Enigma:
Alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ Cipher: BCDEFGHIJKLMNOPQRSTUVWXYZA Example Plaintext Input: RAINY DAY Ciphertext Output: SBJOZ EBZ
The Enigma is basically a composition of substitution ciphers. Different ciphers are repeatedly applied to the plaintext (the number of times is determined by the number of rotors; more on this later). This application of multiple ciphers increases the cryptographic security of the machine, meaning that it is harder for an adversary to break the code. The Enigma takes one letter at a time, applies multiple ciphers to that one letter, and then prints out the final encoded letter. This process is repeated for the whole plaintext message.
This project requires a thorough understanding of the Enigma machine. Please read the Enigma History and Functionality Page . We strongly recommend that you read the information there for full information on the Enigma Maschine functionality. It contains everything you need to know about the Enigma machine, including a full explanation of the rotors, notches, Reflector, and Plugboard.
To begin, you must download the skeleton code file and add it to your Eclipse project. To do this, create an Eclipse project as you have done in lab (we suggest you call this project "P2"). Download the following Java files and extract them into your project as source files.
CAUTION: Be sure that you extract the files into your p2 folder and not into p2/skeleton folder. If you have already extracted the *.java files into p2/skeleton, simply move the *.java files back to p2/*.java and refresh your project.
The zip file contains 2 files: RotorConstants.java and Enigma.java. Do not modify any code in the RotorConstants class. Any code you write will be in the Enigma class.
The code you do write MUST be written within the framework we have provided. That is, you must fill in all the public methods we have provided, and use these methods to implement your program.
Javadoc pages are html files that document Java classes and methods. They are generated by a utility program named javadoc, and they contain uniformly formatted explanations of the class and methods in Java programs. We have generated this documention for you from the comments that we provided in the code skeleton.
Being able to read and understand Javadoc is an important part of this assignment and Java programming. Read through the Javadoc pages. When you first open the link, you will be taken to the class Enigma page. Scroll down to the Method Summary. Here you will find the methods that you are required to implement. Each method has a short summary that displays its method name, return type, and the input parameters of the method, followed by a brief description of the method.
Click on one of the method names. This will take you to the method's full-blown javadoc description. These descriptions describe in detail what each method does, and you are highly encouraged to refer to these javadoc pages early and often.
For this project, you will be implementing a simplified version of the Enigma machine. To do this, you are required to use the skeleton source files we provide and implement the solution we have designed. All code you write must be contained within the framework of our skeleton Enigma class.
You must implement the methods EXACTLY as they appear in the Javadoc and program skeleton.
By this we mean:
You may add additional private methods as you feel are appropriate to complete the assignment.
You will need to edit each method's body so that it implements the functionality described in the Javadoc and program skeleton. When grading, we will be testing the required methods individually to verify that they work as described.
For clarity and understanding, we provide you with several sample runs which exhibit the specified behavior your program should follow. Please refer to these often and examine them closely. The output of your final program should match these exactly.
Please note that all of these sample runs are displaying the behavior of the entire complete program.
These sample runs were generated by using this alternative RotorConstants.java file. You program should be able to be run with alternate rotor constants and if your program is run with this alternate version of RotorConstants.java, it should be able to produce these sample runs.
These sample runs were generated to help you assess the progress of your program milestone-by-milestone. None of them exhibit the same functionality as a complete Enigma.java program
These sample runs were generated to assist you with the extra credit assignment.
All the code you write will be in the main Enigma class. Do not add any code to RotorConstants.java. The only file that you will turn in is: Enigma.java
Start your users off with the following welcome message:
Willkommen auf der Enigma-Maschine
(that's "Welcome to the Enigma Machine" in German.)
Next, give a prompt asking the users for the the Selection and Ordering of Rotors:
Please enter a Rotor Configuration.
This must be a list of numbers in the range from 0 to <N>, separated by spaces.
Note that rotor 0 is the identity rotor.
Where <N> would be replaced with the number of rotor Strings in RotorConstants.ROTORS.
The user should then respond to this by entering a sequence of numbers, separated by spaces. For example, if the user wants to use rotors III, II, and IV (in that order), they would enter "3 2 4".
You must specify at least one rotor.
Invalid rotor. You must enter an integer between 0 and <N>.
Where <N> would be replaced with the number of rotor Strings in RotorConstants.ROTORS.
Example:
If there are 8 rotor Strings in the RotorConstants.ROTORS array, the user must
specify which rotors to use by using the numbers 0 through 7.
In the 8 rotor case, if the user tries to use rotor 8, the error
message would print: Invalid rotor. You must enter an integer
between 0 and 7.
You cannot use the same rotor twice.
Multiple Errors:If the user enters in a sequence which contains both an invalid rotor and a duplicate rotor, the invalid rotor should take precedence, causing ONLY the Invalid Rotor error message to get printed.
Note: Your rotor configuration prompt does not need to handle non-integer input. You may also assume that the rotor configuration input will NOT have any leading or trailing whitespace. You may also assume that each token of input will be separated by exactly one space character.
For any of these errors, your program should terminate without any further output messages or user prompts. It is perfectly acceptable to fulfill this requirement by using System.exit(-1) to hard-exit your program. Later in the course we will discuss more elegant mechanisms, but hard-exiting in this fashion is fine for this project.
Important These error messages should NOT be found and displayed by the code in the main() method itself. They should instead be found and displayed by the parseRotorIndices() method. This is important as we will expect these messages when we test the parseRotorIndices() method independent of main().
After configuring the rotors, your program should next print the prompt
Enter lines of text to be encoded:
After printing the prompt one time, the program should enter the main program loop, which will repeatedly do the following things:
Encoded result: <Ciphertext>
Where <Ciphertext> would be replaced with the encoded result of the plaintext.
Ship unmaneuverable. We shall fight to the last shell. Encoded result: WUST XRQVAWWTOTPLIR. LY ZMWYR HVBNP NH FWL BKDB AUXIQ.
The RotorConstants class provides several one-dimensional and two-dimensional arrays that store configuration information about the rotors. Read the Javadoc pages to learn the details, below we have provided a brief overview:
The RotorConstants class can be thought of as a configuration file for your project. The class contains rotor ciphers that model the Engima M3 model. However, in testing and grading your program, we will link your program with other RotorConstants classes. As such, you are required to write your program in such a way that will work with any RotorConstants class that we define. In particular, please note that:
The Enigma class lays out the required groundwork for your program's design. It specifies a number of methods which you are required to implement and used as described. The required methods are in the code skeleton and described in its javadoc page.
Your enigma machine will need a number of data structures (1D and 2D arrays) in order to keep track of what's going on. Here are the data structures we suggest you use:
We suggest that you incrementally develop this program by following the milestones in the order listed below. This order will make it easy for you to check that each milestone is working properly before you move on to the next one. Don't move on to a new milestone until you can convince yourself that the current milestone is working correctly. The further along you get, the harder it will be to detect errors in code you developed early on.
print command. Is your output not matching the Milestone 4 sample run? Take a glance at the "Encrypting in Reverse Order" section in the History and Functionality page.
Milestone 5 Sample Run
Milestone 5 Sample Run 2
For parsing the rotor indices line (what the user types when the program begins), there are two recommended approaches you may take:
First, use a Scanner object to read in the entire rotor input line as a String.
Then, (inside the parseRotorIndices() method) :
To assist you in debugging, the RotorConstants.ROTORS array will always have a special value stored at its 0th index: the "Identity Rotor". This rotor is simply the alphabet. Using it (before advancement, and not taking into account the Reflector) will cause all characters to map to themselves: 'A' would encode as 'A', 'B' would encode as 'B' etc.
You will have to be able to convert lines of text into UPPERCASE letters. To do this, you can use the String class toUpperCase() method.
To check if a single letter is uppercase, you can use Character.isUpperCase().
Many of the methods will require to work with characters by converting them first to their alphabetical index. In fact, this is one of the primary purposes of the convertRotor() method. The alphabetical index of a letter refers to that letter's position in the alphabet. Suppose we were to create an array of characters in the order of the alphabet:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Index 0 would refer to letter A, index 1 would refer to letter B, etc. Converting a letter into this form is as simple as subtracting 'A' from it.
For this project, it is imperitave that your method implementations follow the interface specified by the Javadoc. You can think of the Javadoc as the contract which your code must adhere to. As such, your code must make use of the input parameters and return values, as specified. All inter-method communication must be through those parameters and return values. As such, using mechanisms like static fields (or "class variables") to avoid passing program information between method would be a violation of the Javadoc that describes how the Enigma class functions.
You won't be explicitly implementing decryption, but your program can indeed decrypt ciphertext! In order to test this functionality, first run your program as usual, encrypting a few different plaintexts. Keep track of what the program outputs as the corresponding ciphertexts. Now, restart your program and use the same rotor configuration (i.e. the same rotor numbers in the same order). Now, you can "encrypt" your ciphertexts that you recorded, and you should get back your original plaintexts. The fact that this is possible is one of the beauties of the Enigma.
However, it is important that you actually quit and restart your program to test decryption. This is because decryption requires that the rotors must have the same offsets as when the encryption took place. We have not provided any functionality to manually reset the offsets. Therefore, the simplest way to test this is to restart the program. This sets the offsets back to 0, which is where they started during encryption.
You should test your program by running it frequently as you develop. It is a worthy goal to always have a compiling program so that you can run it and see what it does. If you follow the milestones outlined above, you should be able to verify whether you have completed each milestone by running your program and interacting with it.
One technique that you can apply to your own testing practices is the idea of Unit Testing. Unit testing essentially is the process of testing small units of code (methods in our case) independent of each another. That is, instead of testing your entire program as a whole, take a small piece of it (like a single method), and test that separately. This might mean short-circuiting your main method temporarily and hard-coding inputs to pass into a given method. Give special thought to boundary cases that may test the limits of your methods' behavior. Off by one errors are easy to make.
To be a good tester, you have to be willing to become your own adversary. Try to break your own code with different input combinations, and do not relent until you are convinced that your program is 100% correct.
The extra credit for this assignment involves implementing another level of the Enigma's security: the plugboard. The plugboard allows pairs of letters to be swapped before and after the rotors do their substitution ciphers. The name "plugboard" comes from the fact that on the physical Enigma machine, the user would plug a wire into the two letters they wanted to be swapped. Our version allows the user to enter on a single line which letters they want to be swapped.
Using the plugboard involves getting one more piece of input from the user. You need to know which pairs of letters should be swapped on the plugboard. Prompt the user for these pairs after they have entered the rotor configuration:
Enter pairs of letters to connect on the plugboard. Separate pairs by spaces:
For example, if the user wanted to swap the letters A and F, as well as C and T, they would enter AF CT at the prompt.
If the input is anything other than what is specified, print the following error message, and exit the program:
Invalid plug pair. You must enter a pair of upper-case letters with no characters in between.
There are three additional methods you will need to implement the plugboard: parsePlugs(), encodeWithPlugs(), and swapPluggedPairs(). The processes of rotor advancement and rotating are unaffected by the use of the plugboard.
Parsing the plugboard input is similar to parsing the rotor configuration. The plug configuration becomes a two-dimensional array, or, an array of 2-element arrays. Each 2-element array contains the integer representation of the two letters that are being swapped.
When encoding with plugs, the pairs are swapped before and after the rotors-reflector-rotors sequence. That is, swapPluggedPairs() is called before running the rotors in forward order, and after the rotors are run in reverse order. That is, you should check to see if your original plaintext character is part of a plugged pair and do the swap, if necessary. The encryption then runs as usual. At the end of the encryption, when you are left with an encrypted character, you should now check to see if that character is a member of a plugged pair, and do the necessary swap. Note: a swap may never be done, may be done only before or after the normal encryption, or may occur both before and after the encryption. It depends on what you specify as your plugged pairs.
Important! Read the EnigmaEC Javadoc to get an idea on how to go about implementing these methods. Just as with the non-ec methods, you are required to provide implementations for these methods. Also as before, you are not allowed to change the method headers in any way.
Because you are required to implement the EC methods exactly as specified, you are required to use the ec skeleton below. Essentially it is just a non-ec skeleton that also contains the methods you are required to implement. We recommend that you copy and paste the method bodies from your non-ec version to this skeleton file, and then start coding the EC assignment from there.
EC Sample Runs can be found in the Sample Runs section.
Before handing in your program, check that you've done the following:
Use the CS302 Forms Page to electronically submit and backup your work:
Submit this file:
You are not submitting any of the other files that were provided to you in the skeleton. You should not have edited any other files, and if you did, you should make sure your code still works with those files as they were provided to you.
If you did Extra Credit, submit these files in addition to the other files you submit:
Make sure you did not put any EC-specific code into any other files. This file, along with the non-EC file submitted earlier, should be enough for us to run your entire EC submission.
For this project, you will have the opportunity to upload your program early to receive feedback from our testing server. The testing server will run a number of test cases against your program and send you an email containing the test results.
To "submit" to the testing server, all you have to do is to upload your code anytime before the due date (following the submission steps outlined above). The testing server will then test your program and send its results to you via email. These results are not grades, and nothing you upload is final. These results are simply testing feedback that can assist you in assessing your program.
Note for submitting EC: To get feedback on EC, simply submit it along with your non-EC code. EC Submissions are not counted separately, so please be sure to submit both files whenever you are handing in EC.
You have the opportunity to receive testing feedback at most once per calendar day. When you submit for the first time on any given calendar day, the testing server will test your program and send back and email report as soon as possible. All subsequent submissions for that day will be "silent."
At the beginning of each calendar day (sometime between 12:01AM and 3:00AM), your most recent "silent" submission from the previous day (if applicable) will be tested and an emailed will be sent. When such an instance occurs, you will not be eligible to receive testing results until the next calendar day.