bgneal@4
|
1 #ifndef CPP_ENIGMA_MACHINE_H
|
bgneal@4
|
2 #define CPP_ENIGMA_MACHINE_H
|
bgneal@4
|
3 // Copyright (C) 2012 by Brian Neal.
|
bgneal@4
|
4 // This file is part of Cpp-Enigma, the Enigma Machine simulation.
|
bgneal@4
|
5 // Cpp-Enigma is released under the MIT License (see License.txt).
|
bgneal@4
|
6 //
|
bgneal@4
|
7 // machine.h - This file contains the main Enigma machine class.
|
bgneal@4
|
8
|
bgneal@4
|
9 #include <memory>
|
bgneal@4
|
10 #include <string>
|
bgneal@4
|
11 #include <vector>
|
bgneal@4
|
12 #include <cassert>
|
bgneal@4
|
13 #include "enigma_types.h"
|
bgneal@4
|
14 #include "rotor.h"
|
bgneal@4
|
15 #include "plugboard.h"
|
bgneal@4
|
16
|
bgneal@4
|
17 namespace enigma
|
bgneal@4
|
18 {
|
bgneal@14
|
19 typedef std::vector<rotor*> rotor_vector;
|
bgneal@4
|
20
|
bgneal@4
|
21 class enigma_machine_error : public enigma_error
|
bgneal@4
|
22 {
|
bgneal@4
|
23 public:
|
bgneal@4
|
24 explicit enigma_machine_error(const std::string& what_arg)
|
bgneal@4
|
25 : enigma_error(what_arg)
|
bgneal@4
|
26 {}
|
bgneal@4
|
27 };
|
bgneal@4
|
28
|
bgneal@4
|
29 class enigma_machine
|
bgneal@4
|
30 {
|
bgneal@4
|
31 public:
|
bgneal@14
|
32 // Construct an Enigma machine from component parts.
|
bgneal@14
|
33 // Note that the enigma_machine makes copies of the rotors and will not
|
bgneal@14
|
34 // delete the rotor pointers:
|
bgneal@11
|
35 enigma_machine(const rotor_vector& rv,
|
bgneal@14
|
36 const rotor& reflector,
|
bgneal@4
|
37 const plugboard& pb);
|
bgneal@4
|
38
|
bgneal@4
|
39 // construct an Enigma machine with a default plugboard (no cables connected):
|
bgneal@11
|
40 enigma_machine(const rotor_vector& rv,
|
bgneal@14
|
41 const rotor& reflector);
|
bgneal@4
|
42
|
bgneal@4
|
43 // key-sheet style constructors:
|
bgneal@4
|
44 enigma_machine(const std::vector<std::string>& rotor_types,
|
bgneal@4
|
45 const std::vector<int>& ring_settings,
|
bgneal@4
|
46 const std::string& reflector_name = "B",
|
bgneal@4
|
47 const std::string& plugboard_settings = "");
|
bgneal@4
|
48
|
bgneal@4
|
49 // set the rotor display (starting position) - 3 rotor version
|
bgneal@4
|
50 void set_display(char left, char mid, char right)
|
bgneal@4
|
51 {
|
bgneal@13
|
52 assert(rotors.size() == 4);
|
bgneal@4
|
53
|
bgneal@13
|
54 rotors[1].set_display(left);
|
bgneal@13
|
55 rotors[2].set_display(mid);
|
bgneal@13
|
56 rotors[3].set_display(right);
|
bgneal@4
|
57 }
|
bgneal@4
|
58
|
bgneal@4
|
59 // set the rotor display (starting position) - 4 rotor version
|
bgneal@4
|
60 void set_display(char c0, char c1, char c2, char c3)
|
bgneal@4
|
61 {
|
bgneal@13
|
62 assert(rotors.size() == 5);
|
bgneal@4
|
63
|
bgneal@13
|
64 rotors[1].set_display(c0);
|
bgneal@13
|
65 rotors[2].set_display(c1);
|
bgneal@13
|
66 rotors[3].set_display(c2);
|
bgneal@13
|
67 rotors[4].set_display(c3);
|
bgneal@4
|
68 }
|
bgneal@4
|
69
|
bgneal@7
|
70 // Set the rotor display (starting position) using a string; the
|
bgneal@7
|
71 // string length must match the number of rotors in use or a
|
bgneal@7
|
72 // enigma_machine_error exception will be thrown:
|
bgneal@7
|
73 void set_display(const std::string& val)
|
bgneal@7
|
74 {
|
bgneal@13
|
75 if (val.size() == 3 && rotors.size() == 4)
|
bgneal@7
|
76 {
|
bgneal@7
|
77 set_display(val[0], val[1], val[2]);
|
bgneal@7
|
78 }
|
bgneal@13
|
79 else if (val.size() == 4 && rotors.size() == 5)
|
bgneal@7
|
80 {
|
bgneal@7
|
81 set_display(val[0], val[1], val[2], val[3]);
|
bgneal@7
|
82 }
|
bgneal@7
|
83 else
|
bgneal@7
|
84 {
|
bgneal@7
|
85 throw enigma_machine_error("set_display invalid size");
|
bgneal@7
|
86 }
|
bgneal@7
|
87 }
|
bgneal@7
|
88
|
bgneal@4
|
89 // return the rotor display (starting position) as a string
|
bgneal@4
|
90 std::string get_display() const
|
bgneal@4
|
91 {
|
bgneal@4
|
92 std::string result;
|
bgneal@13
|
93 for (std::size_t i = 1; i < rotors.size(); ++i)
|
bgneal@4
|
94 {
|
bgneal@13
|
95 result += rotors[i].get_display();
|
bgneal@4
|
96 }
|
bgneal@4
|
97 return result;
|
bgneal@4
|
98 }
|
bgneal@4
|
99
|
bgneal@4
|
100 // simulate front panel key press; returns the lamp character that is lit
|
bgneal@4
|
101 char key_press(char c)
|
bgneal@4
|
102 {
|
bgneal@4
|
103 step_rotors();
|
bgneal@4
|
104 return electric_signal(c - 'A') + 'A';
|
bgneal@4
|
105 }
|
bgneal@4
|
106
|
bgneal@15
|
107 // this is like key_press(), but it works in signal numbers (0-25) instead of chars:
|
bgneal@15
|
108 int step(int n)
|
bgneal@15
|
109 {
|
bgneal@15
|
110 step_rotors();
|
bgneal@15
|
111 return electric_signal(n);
|
bgneal@15
|
112 }
|
bgneal@15
|
113
|
bgneal@4
|
114 // Process a buffer of text of length n, placing the result in an output buffer.
|
bgneal@4
|
115 void process_text(const char* input, char* output, std::size_t n)
|
bgneal@4
|
116 {
|
bgneal@4
|
117 for (std::size_t i = 0; i < n; ++i)
|
bgneal@4
|
118 {
|
bgneal@4
|
119 *output++ = key_press(*input++);
|
bgneal@4
|
120 }
|
bgneal@4
|
121 }
|
bgneal@4
|
122
|
bgneal@4
|
123 std::string process_text(const std::string& input)
|
bgneal@4
|
124 {
|
bgneal@4
|
125 std::string result;
|
bgneal@4
|
126 result.reserve(input.size());
|
bgneal@4
|
127
|
bgneal@4
|
128 for (const auto& c : input)
|
bgneal@4
|
129 {
|
bgneal@4
|
130 result += key_press(c);
|
bgneal@4
|
131 }
|
bgneal@4
|
132 return result;
|
bgneal@4
|
133 }
|
bgneal@4
|
134
|
bgneal@12
|
135 // Process a buffer of pre-processed text of length n, placing the result in an output buffer.
|
bgneal@12
|
136 void process_data(const char* input, char* output, std::size_t n)
|
bgneal@12
|
137 {
|
bgneal@12
|
138 for (std::size_t i = 0; i < n; ++i)
|
bgneal@12
|
139 {
|
bgneal@12
|
140 step_rotors();
|
bgneal@12
|
141 *output++ = electric_signal(*input++) + 'A';
|
bgneal@12
|
142 }
|
bgneal@12
|
143 }
|
bgneal@12
|
144
|
bgneal@4
|
145 // for access to the plugboard for hill-climbing, etc
|
bgneal@4
|
146 plugboard& get_plugboard() { return pb; }
|
bgneal@4
|
147
|
bgneal@8
|
148 // Returns a string representation of the enigma machine's state. Useful
|
bgneal@8
|
149 // for logging, etc:
|
bgneal@8
|
150 //
|
bgneal@8
|
151 std::string army_str() const { return str(true); }
|
bgneal@8
|
152 std::string navy_str() const { return str(false); }
|
bgneal@8
|
153
|
bgneal@4
|
154 private:
|
bgneal@13
|
155 // Note that to improve cache performance, the rotors and reflectors are stored
|
bgneal@13
|
156 // in a contiguous vector.
|
bgneal@13
|
157 std::vector<rotor> rotors; // rotor & reflector array
|
bgneal@4
|
158 plugboard pb;
|
bgneal@4
|
159 rotor* r_rotor; // rightmost rotor
|
bgneal@4
|
160 rotor* m_rotor; // 2nd to right rotor
|
bgneal@4
|
161 rotor* l_rotor; // 3rd to right rotor
|
bgneal@4
|
162
|
bgneal@4
|
163 void rotor_count_check();
|
bgneal@4
|
164
|
bgneal@4
|
165 void step_rotors()
|
bgneal@4
|
166 {
|
bgneal@4
|
167 // The right-most rotor's right-side ratchet is always over a pawl, and
|
bgneal@4
|
168 // it has no neighbor to the right, so it always rotates.
|
bgneal@4
|
169 //
|
bgneal@4
|
170 // The middle rotor will rotate if either:
|
bgneal@4
|
171 // 1) The right-most rotor's left side notch is over the 2nd pawl
|
bgneal@4
|
172 // or
|
bgneal@4
|
173 // 2) It has a left-side notch over the 3rd pawl
|
bgneal@4
|
174 //
|
bgneal@4
|
175 // The third rotor (from the right) will rotate only if the middle rotor
|
bgneal@4
|
176 // has a left-side notch over the 3rd pawl.
|
bgneal@4
|
177 //
|
bgneal@4
|
178 // Kriegsmarine model M4 has 4 rotors, but the 4th rotor (the leftmost)
|
bgneal@4
|
179 // does not rotate (they did not add a 4th pawl to the mechanism).
|
bgneal@4
|
180
|
bgneal@4
|
181 const bool l_rotate = m_rotor->notch_over_pawl();
|
bgneal@4
|
182 const bool m_rotate = l_rotate || r_rotor->notch_over_pawl();
|
bgneal@4
|
183
|
bgneal@4
|
184 r_rotor->rotate();
|
bgneal@4
|
185 if (m_rotate)
|
bgneal@4
|
186 {
|
bgneal@4
|
187 m_rotor->rotate();
|
bgneal@4
|
188 }
|
bgneal@4
|
189 if (l_rotate)
|
bgneal@4
|
190 {
|
bgneal@4
|
191 l_rotor->rotate();
|
bgneal@4
|
192 }
|
bgneal@4
|
193 }
|
bgneal@4
|
194
|
bgneal@4
|
195 // Simulate running an electric signal through the machine in order to
|
bgneal@4
|
196 // perform an encrypt or decrypt operation
|
bgneal@4
|
197 // signal_num - the wire (0-25) that the simulated current occurs on
|
bgneal@4
|
198 // Returns a lamp number to light (an integer 0-25).
|
bgneal@4
|
199 int electric_signal(int signal_num)
|
bgneal@4
|
200 {
|
bgneal@13
|
201 int n = pb.signal(signal_num);
|
bgneal@4
|
202
|
bgneal@13
|
203 if (rotors.size() == 4) // 3 rotors + reflector
|
bgneal@4
|
204 {
|
bgneal@13
|
205 n = rotors[3].signal_in(n);
|
bgneal@13
|
206 n = rotors[2].signal_in(n);
|
bgneal@13
|
207 n = rotors[1].signal_in(n);
|
bgneal@13
|
208 n = rotors[0].signal_in(n); // reflector
|
bgneal@13
|
209 n = rotors[1].signal_out(n);
|
bgneal@13
|
210 n = rotors[2].signal_out(n);
|
bgneal@13
|
211 n = rotors[3].signal_out(n);
|
bgneal@4
|
212 }
|
bgneal@13
|
213 else // Kriegsmarine 4 rotor + reflector
|
bgneal@4
|
214 {
|
bgneal@13
|
215 n = rotors[4].signal_in(n);
|
bgneal@13
|
216 n = rotors[3].signal_in(n);
|
bgneal@13
|
217 n = rotors[2].signal_in(n);
|
bgneal@13
|
218 n = rotors[1].signal_in(n);
|
bgneal@13
|
219 n = rotors[0].signal_in(n); // reflector
|
bgneal@13
|
220 n = rotors[1].signal_out(n);
|
bgneal@13
|
221 n = rotors[2].signal_out(n);
|
bgneal@13
|
222 n = rotors[3].signal_out(n);
|
bgneal@13
|
223 n = rotors[4].signal_out(n);
|
bgneal@4
|
224 }
|
bgneal@13
|
225 return pb.signal(n);
|
bgneal@4
|
226 }
|
bgneal@8
|
227
|
bgneal@8
|
228 std::string str(bool army) const;
|
bgneal@4
|
229 };
|
bgneal@4
|
230 }
|
bgneal@4
|
231
|
bgneal@4
|
232 #endif
|