30 Assignment4 PDF
30 Assignment4 PDF
Assignment #4—Cryptography
YEAH hours: 7:00–8:30 P.M., Wednesday, February 10, TBA
Due: Wednesday, February 17, 5:00 P.M.
Note: This assignment may be done in pairs
Last year’s Hollywood blockbuster The Imitation Game (which won the Academy Award
for Best Adapted Screenplay even though the writers got most of the technical details
wrong) showed movie audiences how a group of cryptographers in England managed to
break the German military code, which was called Enigma. Your task in this assignment
is to replicate some of their work. You, however, are fortunate in having access to
modern computing, allowing you to use Java instead of primitive electronics.
This assignment is divided into three parts, which are substantially independent even
though you will be able to reuse some code. This handout focuses on what you have to
do to complete the assignment. The background you need on cryptography and the
Enigma machine appears in Handout #29.
The key in a letter-substitution cipher is a string that shows the enciphered counterpart of
each of the 26 letters of the alphabet in order. In this example, the user has entered the
key "QWERTYUIOPASDFGHJKLZXCVBNM" (rather unimaginatively generated by typing the
keys in order on the keyboard), which corresponds to the following mapping:
The W at the beginning of the plaintext string maps into V in the ciphertext, the O maps
into G, and so on.
–2–
The translation of an uppercase letter into its ciphertext equivalent is implemented in the
following line from the encrypt method:
ch = key.charAt(ch - 'A');
The expression ch - 'A' subtracts the character code for the letter A from the character
stored in the variable ch, which is guaranteed by the logic of the program to be an
uppercase character. The result is therefore an integer between 0 and 25 indicating the
index of the character ch from the beginning of the alphabet. Calling key.charAt with
this index returns the character at the corresponding position in the key.
–3–
As the comments at the beginning of Figure 1 indicate, the only thing you have to do for
this part of the assignment is add code to check whether the key is legal. A key string in
a letter-substitution cipher is valid only if it meets the following two conditions:
1. The key is exactly 26 characters long.
2. Every uppercase letter appears in the key.
These conditions automatically rule out the possibility that the key contains invalid
characters or duplicated letters. After all, if all 26 uppercase letters appear and the string
is exactly 26 characters long, there isn’t room for anything else.
Your submitted version of the code for LetterSubstitutionCipher.java must ask the
user for a key, check the key to make sure it is legal. If it is not, your program should tell
the user that the key is invalid and allow the user to try again. Your code, for example,
should be able to duplicate the following sample run:
The first key entered by the user is only eight characters long and is therefore too short to
be a legal key. The second attempted key is the right length but is missing the letter Z.
The user gets it right on the third try.
The translation table shows that A maps into Q, B maps into W, C maps into E, and so on.
To turn the encryption process around, you have to read the translation table from bottom
to top, looking to see what plaintext letter gives rise to each possible letter in the
ciphertext. For example, if you look for the letter A in the ciphertext, you discover that
the corresponding letter in the plaintext must have been K. Similarly, the only way to get
–4–
a B in the ciphertext is to start with an X in the original message. The first two entries in
the inverted translation table therefore look like this:
If you continue this process by finding each letter of the alphabet on the bottom of the
original translation table and then looking to see what letter appears on top, you will
eventually complete the inverted table, as follows:
The inverted key is simply the 26-letter string on the bottom row, which in this case is
"KXVMCNOPHQRSZYIJADLEGWBUFT".
that generates a decryption key for the encryption key passed as the argument. For
example, if you were to call
invertKey("QWERTYUIOPASDFGHJKLZXCVBNM")
You also need to write a run method that asks the user for an encryption key, displays the
inverted decryption key, and then validates the result by taking advantage of the fact that
inverting a key twice gives you back the original key. The following sample run
produces the last line by calling invertKey on the value shown on the preceding line:
Make sure your solution checks that the key is legal, just as you did in Part 1.
This part of the assignment requires you to implement two classes: EnigmaSimulator
and EnigmaModel. The subsections that follow describe how the classes needed for
Part 3 fit together and what you need to do to implement them.
–5–
This example shows that, given the rotor order 513 and rotor setting "JLY", the plaintext
"HELLOWORLD" gets enciphered into "OYCEMFIYHY". That string seems entirely
meaningless, but the magic of the Enigma machine is that typing the enciphered message
into a machine that starts with the same settings reproduces the original plaintext string,
as follows:
The EnigmaSimulator class itself is simple to write because the EnigmaModel class does
most of the hard work. The only requirement for this class beyond implementing the
steps described earlier in this section is that your program should allow the user to reenter
the rotor order and rotor setting values if the user enters an illegal value. The actual
checking for legality is done by the EnigmaModel class, but your implementation has to
notice that EnigmaModel class has rejected one of these values and then allow the user to
try again.
–6–
The public entries in the EnigmaModel class appear in tabular form in Figure 3. The first
entry is the constructor, and the rest are the public methods. The EnigmaModel.java file
that comes with the starter project includes a full set of javadoc comments for the
constructor and the public methods but the implementations of those methods have been
left up to you. Those incomplete methods—which in some cases include temporary code
to ensure that the methods return a value of the appropriate type—are generally called
stubs. For example, the stub for getRotorSetting is
public String getRotorSetting() {
return ""; // Replace this line with the actual implementation
}
The stub returns the empty string just to make sure that the program compiles correctly.
Figure 2. Public entries in the EnigmaModel class
new EnigmaModel() Creates a new instance of the EnigmaModel class, which
simulates the operation of the Enigma machine. By
default, the constructor uses the first three stock rotors as
the slow, medium, and fast rotors.
setRotorOrder(order) Sets the rotor order to order, which is a three-digit integer
giving the numbers of the stock rotors to use. For
example, calling setRotorOrder(513) uses stock rotor 5
as the slow rotor, stock rotor 1 as the medium rotor, and
stock rotor 3 as the fast rotor. The method returns true if
the rotor order is legal (the digits are between 1 and 5 and
no digit appears more than once) and false otherwise.
setRotorSetting(str) Sets the rotor setting to str, which must consist of three
uppercase letters. The method returns true if the rotor
setting is legal and false otherwise.
getRotorSetting() Returns the three-letter string that corresponds to the
current rotor setting, which advances every time a key is
pressed.
encrypt(plaintext) Encrypts the plaintext string by passing each letter through
the various rotors of the Enigma machine. All letters in
the string are converted to uppercase, and the rotors of the
Enigma machine are advanced before translating each
letter. If a character in the plaintext string is not a letter,
the rotors do not advance and the character is simply
copied to the output unchanged.
–7–
The starter file also includes private definitions for the five stock rotors, each of which is
a 26-character string that specifies the permutation that rotor performs when a signal
flows through it from right to left. For example, the definition of STOCK_ROTOR_1 is
private static final String STOCK_ROTOR_1 =
"EKMFLGDQVZNTOWYHXUSPAIBRCJ";
Similarly, the starter file contains a definition of the permutation for the Enigma reflector.
Your primary task in Part 3 is to implement the constructor and the four public methods
in the EnigmaModel class. To do so, you need to decide what instance variables are
required to store the state of the Enigma machine and what private methods would help
make your implementation easier to write.
Figure 4. Initial setting of the Enigma machine using the first three stock rotors
–8–
Ignoring for the moment the fact that the rotors advance whenever a key is pressed, each
character runs through a fixed series of seven encryption steps. Pressing a key causes an
electrical signal to be fed into the corresponding wire at the right edge of the machine.
That signal then passes through the fast rotor, where it gets shifted to some other position
in the alphabet. From there, the signal flows through the medium rotor, the slow rotor,
and the reflector. It then makes a return trip through the rotors in left-to-right order. The
complete path is shown in Figure 5, which shows the signal starting at A and ending at R.
Each individual stage in the translation is nothing more than a simple letter-substitution
cipher. The first stage, for example, energizes the A wire at the right of the fast rotor,
which implements the following permutation:
The signal emerges from the fast rotor at the B position and then moves on through the
medium rotor, where it is translated into J:
These three translation steps take the signal up to the reflector, which you can see in the
highlighted path in Figure 5.
The signal then goes back through the three rotors in the opposite direction. When the
signal is running from left to right, however, you can’t use the permutations directly
because the translation has to happen “backwards.” What you need, therefore, is the
inverse of the original permutation. The good news is that you’ve already written
invertKey for Part 2 and can use the same code here. The return trip through the slow,
medium, and fast rotors therefore looks like this:
Physically, the operation of advancing a rotor one notch corresponds to turning the rotor
one position so that the next letter appears in the window. Simulating that operation in
the underlying implementation is a two-step process. In the first step, the permutation
string associated with the rotor shifts by one character position, cycling the original first
character to the end of the string. In the second step, every character in the new string
must move one step closer to the beginning of the alphabet, so that Z becomes Y, Y
becomes X, and so on. This operation is also cyclical, which means that the A in the
permutation string becomes Z.
– 10 –
For example, the first stock rotor uses the following permutation string when it is in the A
position, which is simply the definition of the STOCK_ROTOR_1 constant:
"EKMFLGDQVZNTOWYHXUSPAIBRCJ"
If you rotate this rotor to the B position, the first step is to move the first letter to the end
of the string, which produces the following permutation:
"KMFLGDQVZNTOWYHXUSPAIBRCJE"
The second step in the process is to create the correct permutation string by subtracting
one from the internal values of each of these characters so that it becomes the preceding
letter, letting A wrap around to Z. The updated permutation string is therefore
"JLEKFCPUYMSNVXGWTROZHAQBID"
These operations are straightforward to implement using the methods in Java’s String
class, but you are unlikely to get this part right unless you test your code as you go. You
need to create a method
private String advanceRotor(String permutation)
that takes a permutation string and returns the new permutation string that results from
performing these two operations. Write a test program and make sure that calling
advanceRotor("EKMFLGDQVZNTOWYHXUSPAIBRCJ") gives you back the string
"JLEKFCPUYMSNVXGWTROZHAQBID".
Keeping track of the current permutation implemented by each rotor does not give you
quite enough information to implement all the operations in the EnigmaModel class.
Several of the methods need to keep track of what letter appears in the window on the top
panel of the Enigma. That information cannot be determined from the current value of
the permutation string. After advancing the rotor once, your implementation needs to
know that it is in the B position, but there is no way to figure that out just from looking at
the string "JLEKFCPUYMSNVXGWTROZHAQBID". Keeping track of the current position of
each rotor in an instance variable will make your life much easier.
The other operations in the EnigmaModel class are generally easier to implement than
encrypt, although you will need to think carefully about your implementation strategies.
The following tips will also probably help you do well on this assignment:
• Try to get into the spirit of the history. Although this project is an assignment in 2016,
it may help to remember that the outcome of World War II once depended on people
solving precisely this problem using tools that were far more primitive that the ones
you have at your disposal. Computing sometimes matters a great deal, and there will
probably be situations in your lifetimes when the consequences of solving some
programming challenge will be just as important. The fate of the world may some day
lie in your hands, just as it did for the cryptographers at Bletchley Park.
• Draw lots of diagrams. Understanding how the Enigma machine works is an exercise
in visualizing how the machine works. A picture will be worth a thousand words here.
• Test your program in pieces. Don’t try to get Part 3 of this assignment running all at
once. Before you figure out how to advance the rotors on each key press, make sure
that you can successfully encrypt a character given a fixed position of the rotors. Once
you have that working, you can move on to other tasks. You should also make sure to
test the advanceRotor method described in the previous section separately before you
integrate it into the application as a whole.
• Debug your program by seeing what values appear in the variables. When you are
debugging a program, it is far more useful to figure out what your program is doing
than trying to determine why it isn’t doing what you want. Every part of this
assignment works with strings, and you can get an enormous amount of information
about what your program is doing by using printing out the value of the strings you’re
using at interesting points. Unfortunately, the EnigmaModel class doesn’t have direct
access to the program console, but you can nonetheless display data on the Eclipse
console by calling System.out.println.
• Check your answers against the demonstration programs. The class web site includes
working versions of each part of this program. Your code should generate the same
ciphertext given a particular rotor order, rotor setting, and plaintext string. The web
site also has a graphical simulator for the Enigma machine.
• Come to office hours. The Enigma simulator is a new assignment this quarter, which
means that the section leaders may not know exactly how everything works until
they’ve had a chance to play with it. In addition to my regular office hours (Tuesdays
from 9:30 to 11:00 and Wednesdays from 4:00 to 5:00), I will drop by the LaIR from
time to time to help out.
– 12 –
Possible extensions
There are many things you can implement to make this assignment more challenging.
Here are just a few ideas:
• Add graphics. If you like working with graphics, you could change the main program
so that it simulates the operation of the Enigma machine on the screen. If you do, you
shouldn’t have to change the EnigmaModel class at all. Whenever you get a mouse
click in one of the Enigma keys you’ve displayed on the screen, you can simply pass
that character to encrypt as a one-character string and then take the character you get
back and use that to determine which lamp to light. The overall design of the program
uses a programming pattern called MVC (for model-view-controller) in which the
responsibilities of different classes are divided among these three roles. The
EnigmaModel class is (naturally enough) the model, which keeps track of the internal
state. The graphics code you write and the main program implement the view and the
controller roles.
• Implement other parts of the Enigma machine. The German wartime Engima designs
are actually more complicated than the model presented here. In particular, the
Enigma machines used by the military branches included a plugboard (Steckerbrett in
German) that swapped pairs of letters before they were fed into the rotors and after
they came out.
• Simulate the actions of the Bombe decryption machine. This assignment has you build
a simulator for the German Enigma machine. Constructing a mechanical simulator,
which the Bletchley cryptographers called a Typex machine, was critical to the
decryption effort, but was not as important as the Bombe, which went through many
possible rotor settings mechanically looking for ones that would generate the correct
patterns, as described in the extended notes on the Enigma machine. You’ll have a
chance to implement the search for crashes in section, but you might also try to figure
out more about how the Bombe worked and build more of its pieces.