# A1: Enigma **Deadline:** Wednesday, 09/07/16, 11:59 pm *This assignment is to be done as individuals, not with partners nor with teams. Please review the [course policies on academic integrity][ai].* In this assignment, you will develop a software replica of the Enigma encryption machine used by the German military in World War II. Efforts to break the Enigma cipher led to the development of the first electronic computers at Bletchley Park, England: the Bombe (designed by Alan Turing) and its successor, Colossus (designed by Tommy Flowers). Using computers, the Allies were eventually able to break the Enigma code, giving them an intelligence edge that changed the balance of the war. ## Introduction to the Enigma The Enigma machine implemented a *substitution cipher*, which encrypts a message by substituting one character for another. Such ciphers go back at least as far as Julius Caesar, who used a simple substitution cipher to encrypt military orders. The Caesar cipher specified that each letter in the alphabet would be encrypted using the letter three positions later in the alphabet. For example, 'A' would be encrypted as 'D', 'B' would be encrypted as 'E', 'C' would be encrypted as 'F', and so on. The code wraps around at the end of the alphabet, so 'X', 'Y' and 'Z' would be encrypted as 'A', 'B', and 'C', respectively. Although the Caesar cipher was effective in its time (when very few people could read at all), its simple pattern of encrypting letters seems pretty obvious today. A *keyed* substitution cipher is more effective. The *key* is a secret presumably known only to the message sender and receiver; it defines the mapping between a *plaintext* letter and a *ciphertext* letter. We can write the key as a 26-character string, in which the first character is the encryption of 'A', the second character is the encryption of 'B', and so on. The key for the Caesar cipher would thus be "DEFGHIJKLMNOPQRSTUVWXYZABC":  Plaintext letter: ABCDEFGHIJKLMNOPQRSTUVWXYZ | V Ciphertext letter: DEFGHIJKLMNOPQRSTUVWXYZABC  Of course, there are many other possible keys: since there are 26! (roughly 4 \* 10^26) different arrangements of the 26 letters in the alphabet, there are 26! different keys and so 26! different keyed substitution ciphers. Nonetheless, this simple type of encryption is vulnerable to statistical attacks, as anyone who has solved cryptogram puzzles can attest. To implement a substitution cipher, the Enigma machine utilized interchangeable *rotors* (called *wheels* in the diagram below). When placed side-by-side on a *spindle*, the rotors would define an even more complex substitution pattern, with the wires inside each rotor connecting to its neighboring rotors. And a *plugboard* was used to swap pairs of letters, increasing the amount of substitution. When the operator typed a key on the Enigma keyboard, an electrical current would flow through the plugboard to the rotors, through a reflector, then back through the rotors and plugboard, thus forming a closed circuit and lighting up the letter specified by the substitution pattern effected by the plugboard, rotors, and reflector. Here is a diagram of that circuit (the *static wheel* is of no interest to us): <img style="display: block; margin-left: auto; margin-right: auto" src="wiringdiagram.png"/> <p style="text-align:center;">image source: [http://enigma.louisedade.co.uk/wiringdiagram.png]()</p> What made the Enigma cipher especially difficult to break was that just after the operator typed a letter, and just before that letter was enciphered, the rotors moved, changing the substitution pattern. Thus, the letter 'E' might get mapped to a 'Q' the first time it was encountered, but to a completely different letter each successive time. The rightmost rotor shifted for every letter typed, and the other rotors shifted conditionally based on *notches* on the rotors: when a rotor shifted past a notch, not only did that rotor shift, but also the rotor to its left. The notch was not visible to the operator, but the letter that would be on top of the rotor at the point in time when the notch engaged was visible and was known as the *turnover*. The reflector was always inserted at the leftmost side of the spindle. A keypress caused current to flow right-to-left through the rotors, through the reflector, then back through the rotors, left-to-right. The reflector's substitution pattern caused the entire circuit to be symmetric, meaning that if 'E' mapped to 'Q' in a particular machine state, then 'Q' would likewise be mapped to 'E' in that same state. The minor advantage of this design was that the machine did not need separate modes of operation for decryption and encryption. But it turned out to be a major cryptographic weakness, enabling the code to be cracked. The original Enigma machines used at the beginning of World War II had three rotors, known by the Roman-numeral designations I, II, and III, which could be placed on the spindle in any order. The rotors had the letters A..Z appearing on them, in order, and there was a window in the machine through which the operator could observe the top letter showing on each rotor. As the rotors stepped, the top letter changed. When a rotor was initially installed, the operator could manually rotate it to be in any of the 26 possible orientations. There was a single standard reflector called B. Later, other rotors and reflectors were put into use, and machines were built that could have more than three rotors inserted at a time. The plugboard could have up to 13 wires inserted in it. A wire inserted between two letters caused those letters to be swapped. Even though it seems simpler than a rotor, the plugboard was a major source of cryptographic strength for the Enigma cipher. (Note: we are omitting one more Enigma mechanism, the *ring setting*. For aficionados of the Enigma, you may assume the ring setting is always 'A' for each rotor.) ##### Resources for learning more about the Enigma cipher * The [Enigma Cipher Machine][sale] website constructed by Tony Sale, original curator of the Bletchley Park Museum, contains a useful tutorial on how the Enigma cipher works. You should consider [page 1][sale1] and [page 2][sale2] on that site to be required reading. Also read the [worked example][sale-ex], paying special attention to the Technical Note at the end of that example. * To experiment with a virtual Enigma machine, see the [Enigmaco Simulator][enigmaco], which helpfully depicts all the wiring of the machine. You can probably find many other simulators, too. * If you're into arts and crafts, you can even make paper models of an Enigma machine. [This one][paperstrip] is two dimensional hence easy to assemble, but it can be difficult to understand the rotor wiring and stepping with it; [this one][paperring] is three dimensional and may be hard to assemble but gives excellent insight into the physical machine, especially the wiring of the rotors and the reflector. To assemble it, you need to buy a tube of Pringles potato chips, print out the paper at 100% scaling (which can be tricky because it's A4; to help with that we provide these close-ups [[1]][p1] [[2]][p2] of the important parts that should be printable on letter paper), then cut out the paper pieces and wrap them around the tube. [sale]: http://www.codesandciphers.org.uk/enigma/ [sale1]: http://www.codesandciphers.org.uk/enigma/enigma1.htm [sale2]: http://www.codesandciphers.org.uk/enigma/enigma2.htm [sale-ex]: http://www.codesandciphers.org.uk/enigma/example1.htm [enigmaco]: http://enigmaco.de/ [paperstrip]: paper2d.pdf [paperring]: paper3d.pdf [p1]: p1.pdf [p2]: p2.pdf ## Objectives * Gain familiarity with basic OCaml language features, including built-in data types, lists, and functions. * Practice writing programs in the functional style using immutable data. * Improve skills in computational thinking by learning about and modeling a mildly complex, real-world computational artifact. ## Recommended reading * [Lectures and recitations 1&ndash;4][web] * The [CS 3110 Style Guide][style] * The policies about deadlines, extensions, and late submissions in the [course syllabus][late] [web]: http://www.cs.cornell.edu/Courses/cs3110/2016fa/ [style]: http://www.cs.cornell.edu/Courses/cs3110/2016fa/handouts/style.html [syllabus]: http://www.cs.cornell.edu/Courses/cs3110/2016fa/syllabus.php [ai]: http://www.cs.cornell.edu/Courses/cs3110/2016fa/syllabus.php#ai [late]: http://www.cs.cornell.edu/Courses/cs3110/2016fa/syllabus.php#late ## Requirements Your task in this assignment is to simulate the operation of an Enigma machine. * Your solution must provide the functions described below, with exactly those names and types. * Your solution must correctly implement the Enigma cipher, including the spindle, rotors, reflector, turnovers, starting positions, and plugboard of the Enigma machine. * Your code must be written with good style and be well documented. * You must submit an OUnit test suite, as described below. ## What we provide In the release code on the course website you will find these files: * A template file enigma.ml with some function stubs. * A template file enigma_test.ml with some OUnit stubs. * A template file a1.txt for submitting your written feedback. * Some additional scripts for compiling your code. ## Grading issues * **Late submissions:** Carefully review the course policies on [submission and late assignments][late]. Verify before the deadline on CMS that you have submitted the correct version. * **Environment, names, and types:** Your solution must compile and run under OCaml 4.03.0 with OUnit 2.0.0. You are required to adhere to the names and types of the functions specified below. Your solution must pass the make check described below in Part 0. Otherwise, your solution will receive minimal credit. * **Code style:** Refer to the [CS 3110 style guide][style]. Ugly code that is functionally correct will nonetheless be penalized. Take extra time to think and find elegant solutions. ## Prohibited OCaml features You may not use imperative data structures, including refs, arrays, mutable fields, and the Bytes and Hashtbl modules. Strings are allowed, but the deprecated (mutable) functions on them in the String module are not. You have not seen these features in lecture or recitation, so we doubt you'll be tempted. Your solutions may not require linking any additional libraries/packages beyond OUnit. ## Part 0: Sanity check Download the release code. Run make check from the folder containing the release code. You should get output that includes the following (other lines might be interspersed):  =========================================================== Your OCaml environment looks good to me. Congratulations! =========================================================== Your function names and types look good to me. Congratulations! ===========================================================  If instead you get warnings about your OCaml environment, seek help immediately from a consultant: something is wrong with the version of OCaml you have installed. Assuming your environment is good, you should not get warnings about about your function names and types at this point, because you haven't modified the provided file enigma.ml yet. As you complete the assignment, you will be modifying that file. As you solve each part of the assignment, you should re-run make check to ensure that you haven't changed the names or types of any of the required functions. Running simply make will build your OUnit test suite in enigma_test.ml and run it. The test cases in the suite we ship to you will currently both fail, because you haven't yet implemented the index function that they test. Running make clean will delete generated files produced by the make and build systems. ## Part 1: An easy warmup Write a function index that gives the 0-based index of a letter in the alphabet. Below is a specification comment for the function. The brackets around code in the comment is a convention that comes from the OCaml standard library documentation. The line below the comment is an OCaml *value specification* giving the name and type of a value; OCaml would respond with that specification if you entered your function at the toplevel.  (* [index c] is the 0-based index of [c] in the alphabet. * requires: [c] is an uppercase letter in A..Z *) val index : char -> int  Your solution should need only about one line of code and should make use of the [Char module][char]. We provide two OUnit unit tests for your function, one to check that the index of 'A' is 0, and another to check that the index of 'Z' is 25. Compile and run your test suite with make. Make sure that both the tests pass. Re-run make check to double-check that you haven't changed the names and types of any required functions; continue to do that after solving each part. [char]: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Char.html ## Part 2: Rotor wiring Let's suppose current is flowing right to left. It enters a rotor on the right-hand side from one of 26 positions, and the rotor wiring conveys that current to the left-hand side, where it exits in one of 26 positions. The map from input position to output position is determined by two things: the wiring of the rotor, and the orientation of the rotor. In literature on Enigma, the wiring of a rotor is often specified as a 26-character string. For example, the standard rotors I, II, and III from the 1930 Enigma I are:  rotor I: EKMFLGDQVZNTOWYHXUSPAIBRCJ rotor II: AJDKSIRUXBLHWTMCQGZNPYFVOE rotor III: BDFHJLCPRTXVZNYEIWGAKMUSQO  We define a *valid* wiring specification as one that is a 26-character upper-case string and that is a permutation of the letters of the alphabet. Wiring specifications assume that the rotor is installed in the *default* orientation such that 'A' appears through the window to the operator as the top letter. In that orientation, when current is flowing right-to-left, a rotor would map current from right-hand input position \$$i\$$ to left-hand output position \$$j\$$, where \$$j\$$ is the index in the alphabet of the letter appearing at index \$$i\$$ in the wiring specification. (All indices are zero-based.) For example, in the default orientation current entering rotor I from right-hand input position 0 would flow to left-hand output position 4. *(It's tempting to think of that as "rotor I encrypts 'A' to 'E'", but that's only true when the rotor is in its default orientation. After the rotor has stepped, it's no longer true. So we encourage you to think in terms of positions rather than letters.)* When current flows left-to-right, the map is inverted: the rotor would map current from left-hand input position \$$i\$$ to right-hand output position \$$j\$$, where \$$j\$$ is the index in the wiring specification at which appears the letter of the alphabet whose index is \$$i\$$ . For example, in the default orientation current entering rotor I from left-hand input position 0 would flow to right-hand output position 20. Of course, rotors are not necessarily installed in their default orientation, and even if they are, the machine's stepping mechanism will cause them to leave that orientation. So we have to adjust for the orientation of the rotor in determining which input positions map to which output positions. We leave it up to you to figure out how to do that. Write a function map_r_to_l that computes how a rotor maps current from right to left, and a corresponding function map_l_to_r for left to right. Note that these functions do not perform any stepping of the rotor; rather, they model how current passes through the rotor when it's in a particular orientation. We'll implement stepping later. Here are specifications for the two functions:  (* [map_r_to_l wiring top_letter input_pos] is the left-hand output position * at which current would appear when current enters at right-hand input * position [input_pos] to a rotor whose wiring specification is given by * [wiring]. The orientation of the rotor is given by [top_letter], * which is the top letter appearing to the operator in the rotor's * present orientation. * requires: * - [wiring] is a valid wiring specification. * - [top_letter] is in 'A'..'Z' * - [input_pos] is in 0..25 *) val map_r_to_l : string -> char -> int -> int (* [map_l_to_r] computes the same function as [map_r_to_l], except * for current flowing left to right. *) val map_l_to_r : string -> char -> int -> int  Your solution should need only a few lines of code per function and should make use of the [String module][string]. You will likely discover that you want to implement a modulo operator that differs from the built-in operator mod, and that you want to implement a function that is the inverse of index, which you wrote in Part I. Write OUnit unit tests for your map_r_to_l and map_l_to_r function. Add those unit tests (and all others you create in future Parts of this assignment) to enigma_test.ml. Here are some ideas to get you started: * The wiring specification ABCDEFGHIJKLMNOPQRSTUVWXYZ in orientation 'A' should be the identity map, and other orientations should have no effect on the map. You could even test this before you've written any code at all to account for the orientation. * The wiring specification BACDEFGHIJKLMNOPQRSTUVWXYZ in orientations 'A', 'B', and 'C' would be useful to test next, to see whether your code for handling the orientation is correct. * The historical rotors would be good to test next, because you can validate your test cases against the virtual or paper simulators. For example, if rotorIII = "BDFHJLCPRTXVZNYEIWGAKMUSQO", then map_r_to_l rotorIII 'O' 14 should be 17. And if rotorI = "EKMFLGDQVZNTOWYHXUSPAIBRCJ", then map_l_to_r rotorI 'F' 10 should be 14. Please do not assume that rotors I, II and III are the only rotors. We will test your solution with [historical rotors][wiring] and shiny new rotors. [string]: http://caml.inria.fr/pub/docs/manual-ocaml/libref/String.html [wiring]: http://www.cryptomuseum.com/crypto/enigma/wiring.htm ## Part 3: Reflector wiring The wiring of a reflector is also specified as a 26-character string. The standard reflectors B and C from the 1930 Enigma I are:  reflector B: YRUHQSLDPXNGOKMIEBFZCWVJAT reflector C: FVPJIAOYEDRZXWGCTKUQSBNMHL  Unlike rotors, the reflectors do not rotate, so we don't have to worry about their orientation. Also, current flows only one direction through them, so we don't have to worry about right-to-left vs. left-to-right. When current enters a reflector at input position \$$i\$$, it exits at output position \$$j\$$, where \$$j\$$ is the index in the alphabet of the letter appearing at index \$$i\$$ in the wiring specification. For example, current entering reflector B at input position 0 would exit at output position 24. Write a function map_refl that computes how a reflector maps current. Here is a specification for the function:  (* [map_refl wiring input_pos] is the output position at which current would * appear when current enters at input position [input_pos] to a reflector * whose wiring specification is given by [wiring]. * requires: * - [wiring] is a valid wiring specification. * - [input_pos] is in 0..25 *) val map_refl : string -> int -> int  *Hint: could you implement this function in terms of functions you've already implemented?* Write OUnit unit tests for your map_refl function. Add those unit tests to enigma_test.ml. The suggested ideas for rotors unit tests above are good ideas to adapt here. Please do not assume that reflectors B and C are the only reflectors. We will test your solution with [historical reflectors][wiring] and shiny new reflectors. ## Part 4: The plugboard The plugboard swaps pairs of letters before and after current passes through the rest of the machine. The operator configured the plugboard by inserting cables between pairs of letters. We'll represent a plugboard by a list of pairs of characters. For example, here's a plugboard in which two cables have been inserted; one between A and Z, the other between X and Y: [('A','Z'); ('X','Y')]. The order of characters in the pairs should not matter and the order of pairs in the list should not matter. So that plugboard has an effect that is equivalent to this one: [('Y','X'); ('Z','A')]. The plugboard with no cables inserted would be represented by the empty list, []. We define a *valid* plugboard as one in which only the letters A..Z appear, and no letter appears more than once. Write a function map_plug that computes how a plugboard maps letters. Here is a specification for the function:  (* [map_plug plugs c] is the letter to which [c] is transformed * by the plugboard [plugs]. * requires: * - [plugs] is a valid plugboard * - [c] is in 'A'..'Z' *) val map_plug : (char * char) list -> char -> char  *Hint: write a recursive function that pattern-matches against the input list.* Write OUnit unit tests for your map_plug function. Add those unit tests to enigma_test.ml. The empty plugboard and a plugboard that has 13 cables inserted into it would be good bases for test cases. ## Part 5: Ciphering a single character Now it's time to assemble all the functions you've written and tested, and to implement the ciphering of a single character based on the current configuration of the Enigma machine, including the plugboard, the reflector, and the rotors and their orientations. Let's introduce the following type definitions to represent the configuration of a machine:  type rotor = { wiring : string; turnover : char; } type oriented_rotor = { rotor : rotor; top_letter : char; } type config = { refl : string; rotors : oriented_rotor list; plugboard : (char*char) list; }  <span style="color:green;"> [CLARIFICATION 09/04/16] The order of elements in the rotors list in the config type is the same as the order in which the rotors are installed on the spindle, from left to right. For example, in the diagram given in the Introduction to this assignment, the rotors are depicted as being installed in the order I, II, III, from left to right, and that configuration would be represented by the list [rotorI; rotorII; rotorIII], assuming that rotorI, rotorII, and rotorIII have been appropriately defined. </span> We define a *valid* configuration as one in which all the wiring specifications are valid, the plugboard is valid, and the turnovers and top letters are all in A..Z. Note that there might might be any number of rotors in configuration: perhaps 0, perhaps the standard 3, perhaps more. Write a function cipher_char that computes how the Enigma machine ciphers a single letter. Note that this function still does not perform any stepping of the rotors; rather, it models how the machine transforms an input letter into an output letter when all the rotors are in a particular orientation. Here is a specification for the function:  (* [cipher_char config c] is the letter to which the Enigma machine * ciphers input [c] when it is in configuration [config]. * requires: * - [config] is a valid configuration * - [c] is in 'A'..'Z' *) val cipher_char : config -> char -> char  *Hint: consider writing two helper functions,*  map_rotors_r_to_l : oriented_rotor list -> int -> int map_rotors_l_to_r : oriented_rotor list -> int -> int  *which compute the output position that results when current enters and passes through an entire list of rotors. They will be recursive functions over the list. After that, can you factor out a common helper function from them so that there isn't any duplicated code?* Write OUnit unit tests for your cipher_char function. Add those unit tests to enigma_test.ml. Here are some ideas: * Build a configuration that should result in the cipher being the identity function. That will happen when the plugboard is empty, the list of rotors is empty or contains only wiring specifications that are ABCDEFGHIJKLMNOPQRSTUVWXYZ, and the reflector is also wired as ABCDEFGHIJKLMNOPQRSTUVWXYZ. Test to make sure that A ciphers to A, B to B, etc. * The [worked example][sale-ex] on Sale's website that you read at the beginning of this assignment would be a good test case, testing that G ciphers to P in the following configuration: reflector B is installed; rotors I, II, and III are installed in that order from left to right; the top letter of every rotor is A; and the plugboard is empty. * Work out a couple more examples, perhaps with the help of one of the virtual or paper simulators. Code up those examples as unit tests. ## Part 6: Stepping The intuition of stepping is that the rotors behave mostly like the numbers on an odometer: the rightmost completes a full revolution, and in so doing, causes the next rotor to its left to take a single step, and so forth. But the [stepping system on the Enigma][step] is a little more complicated: *Rule 1:* The rightmost rotor always takes a single step just before enciphering each letter. *Rule 2:* Just before every letter is enciphered, if any rotor *except the leftmost* is positioned at its turnover, then that rotor and the rotor to its left steps. *Rule 3:* No rotor steps more than once per letter enciphered, even if the above rules could be construed as suggesting that a rotor would step twice. Some Enigma machines or rotors were built with different stepping rules; in case of discrepancy, the rules above are in effect for this assignment. *If you did the arts-and-crafts project at the beginning of the assignment, note that the [2D paper Enigma machine][paperstrip] in its step 1 under "Operation" is somewhat vague about Rule 2, because it does not say what to do if there does not exist a "rotor to the left."* [step]: http://users.telenet.be/d.rijmenants/en/enigmatech.htm#steppingmechanism Here are the turnovers of some of the historical rotors:  turnover of I: Q turnover of II: E turnover of III: V  **Example 1.** Suppose the installed rotors are III-II-I, starting in orientations where the top letters are KDO. Then as each character is enciphered, they would step as follows: KDO, KDP, KDQ, KER, LFS, LFT, LFU, ... **Example 2.** Suppose the installed rotors are III-II-I, starting with top letters VDP. Then as each character is enciphered, they would step as follows: VDP, VDQ, VER, WFS, WFT, ... Write a function step that computes how the Enigma machine steps just before enciphering a letter. Here is a specification for the function:  (* [step config] is the new configuration to which the Enigma machine * transitions when it steps beginning in configuration [config]. * requires: [config] is a valid configuration *) val step : config -> config  *Observation: the type of this function is a hallmark of the functional style of programming. Instead of mutating state, we produce a new value out of an old one.* *Hint: This is the most algorithmically complex part of the assignment. If you're having difficulty, implement only Rule 1, then move on to the rest of the assignment. Come back and implement Rules 2 and 3 later. Actually, that might be a good strategy in any case.* Write OUnit unit tests for your step function. Add those unit tests to enigma_test.ml. The two examples given above would be a good source of tests. ## Part 7: Enciphering a string At last, it's time to encipher an entire string. Write a function cipher that computes how the Enigma machine ciphers a string. Here is a specification for the function:  (* [cipher config s] is the string to which [s] enciphers * when the Enigma machine begins in configuration [config]. * requires: * - [config] is a valid configuration * - [s] contains only uppercase letters *) val cipher : config -> string -> string  Note that the design of Enigma limits it to the upper-case letters A..Z. Lower-case letters, numerals, punctuation, spaces, etc. are not supported. Write OUnit unit tests for your cipher function. Add those unit tests to enigma_test.ml. Here is one idea: use reflector B, rotors I, II, and III (from left to right), with starting topletters F, U, and N, and an empty plugboard. Encipher YNOXQ; it should produce the name of a pretty good programming language. ## What to turn in Submit files with these names on [CMS][cms]: * enigma.ml, containing your solution code. * enigma_test.ml, containing your OUnit test suite. * a1.txt, containing your written feedback. **REMINDER:** ensure that your solution passes the make check test described in Part 0; if it does not, your solution will receive minimal credit. [cms]: https://cms.csuglab.cornell.edu/ ## Karma *Karma problems* are a way of going above and beyond the call of 3110 duty. Solving them is not required, nor does it earn any explicit extra credit. But the course staff will keep track of the karma problems that you solve throughout the semester. The number of orange camels, below, is an indicator of how hard the karma problem might be. Be mindful that you still need to provide the required functions, above, so make sure your implementation of any karma functionality does not violate the specifications/names/types of those functions. <img src="camel_orange.png" height="40" width="40"> <img src="camel_black.png" height="40" width="40"> <img src="camel_black.png" height="40" width="40"> **Enigmatic:** For additional fun, learn about and implement some of the following: * Ring settings (Ringstellung) * Multiple notches on a rotor (naval rotors VI, VII, and VIII) * The rewirable reflector (Umkehrwalze D) <img src="camel_orange.png" height="40" width="40"> <img src="camel_orange.png" height="40" width="40"> <img src="camel_black.png" height="40" width="40"> **Codebreaker:** Implement a tool that breaks Enigma encryptions without having the key. A brute force attack against a three-rotor machine is probably not that hard. Doing it for a five-rotor machine with plugboard, however, could be an interesting challenge. <img src="camel_orange.png" height="40" width="40"> <img src="camel_orange.png" height="40" width="40"> <img src="camel_orange.png" height="40" width="40"> **Sims:** Implement a virtual simulator using an [OCaml text interface library][text] in which the user can install rotors, adjust the plugboard, encrypt strings, etc. [text]: http://caml.inria.fr/cgi-bin/hump.en.cgi?sort=0&browse=64 * * * **Acknowledgement:** Adapted from Prof. David Reed (Creighton University).