rotor_machine 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9b724da9efeeb1cb707e1b455a0cca97b9a911ce0b8bf11824e786c044135d6
4
- data.tar.gz: 844f9bc228874dc922369d00a3e2ae6caec69c14c63491961e21fb5b32988873
3
+ metadata.gz: 547ab156c985ce0df5dad34665d7705456ff34993b84cc738c887fce7144783f
4
+ data.tar.gz: 6d2211b04113a15757706928e3b572bc09bd2781036fb0317344984f774bd82e
5
5
  SHA512:
6
- metadata.gz: 3da1a7af7b05149bb3ba3bbb2e514e548ea8f6b7727a1b35858a9c381372f01c2cec692e95d20cd0a435b7a21d5cfa1e1f3ec659806aba9862a7c2b7ddfa6900
7
- data.tar.gz: 04a238d1393e0b9ca931f6af190e1813ee9251b299c829ba1df7928a1a94fc2479149576aaef3505fac2ce02d5b1ba7afa347ed3400a2fc500137e15fdac5f76
6
+ metadata.gz: d3d3e224d89bdc1531cfe50f40cf66cec543b693da2547ebf4fd4a3892fc125f5a86c60c96d0912c0161f28e2c353772a5987da99a1d2872d2ee163e49616321
7
+ data.tar.gz: a85cec802ad9a032b8348f5311bd686428d8e23c183182db4dcf9827c831d272b4bb2df1d14716e3749b94928c7c9fd7920d98b18d649ffd6db947d73b17ecf4
data/README.md CHANGED
@@ -2,17 +2,17 @@
2
2
 
3
3
  ![German Enigma machine](https://github.com/tammycravit/rotor_machine/blob/master/images/Bundesarchiv_Enigma.jpg?raw=1)
4
4
 
5
- The `RotorMachine` gem provides a simple Ruby implementation of the
5
+ The `RotorMachine` gem provides a simple Ruby implementation of the
6
6
  [Enigma](https://en.wikipedia.org/wiki/Enigma_machine) rotor encryption machine.
7
7
 
8
- I wrote RotorMachine primarily as an exercise in Test-Driven Development with
9
- RSpec. It is not intended to be efficient or performant, and I wasn't striving much
10
- for idiomatic conciseness. My aims were fairly modular code and a relatively
8
+ I wrote RotorMachine primarily as an exercise in Test-Driven Development with
9
+ RSpec. It is not intended to be efficient or performant, and I wasn't striving much
10
+ for idiomatic conciseness. My aims were fairly modular code and a relatively
11
11
  complete RSpec test suite.
12
12
 
13
- Many thanks to Kevin Sylvestre, whose
14
- [blog post](https://ksylvest.com/posts/2015-01-03/the-enigma-machine-using-ruby)
15
- helped me understand some aspects of the internal workings of the Enigma and
13
+ Many thanks to Kevin Sylvestre, whose
14
+ [blog post](https://ksylvest.com/posts/2015-01-03/the-enigma-machine-using-ruby)
15
+ helped me understand some aspects of the internal workings of the Enigma and
16
16
  how the signals flowed through the pieces of the machine.
17
17
 
18
18
  ## Installation
@@ -33,89 +33,89 @@ Or install it yourself as:
33
33
 
34
34
  ## Architecture
35
35
 
36
- The [`RotorMachine::Machine`](http://www.rubydoc.info/github/tammycravit/rotor_machine/master/RotorMachine/Machine)
36
+ The [`RotorMachine::Machine`](http://www.rubydoc.info/github/tammycravit/rotor_machine/master/RotorMachine/Machine)
37
37
  class serves as the entrypoint and orchestrator for an Enigma machine.
38
-
38
+
39
39
  ### Components of an Enigma machine
40
-
41
- The Enigma machine, as represented by the [RotorMachine](http://www.rubydoc.info/github/tammycravit/rotor_machine/master)
40
+
41
+ The Enigma machine, as represented by the [RotorMachine](http://www.rubydoc.info/github/tammycravit/rotor_machine/master)
42
42
  module, consists of the following components:
43
-
44
- * One or more [rotors](http://www.rubydoc.info/github/tammycravit/rotor_machine/master/RotorMachine/Rotor), which
45
- perform the transposition ciphering and also rotate to produce a polyalphabetic (rather
43
+
44
+ * One or more [rotors](http://www.rubydoc.info/github/tammycravit/rotor_machine/master/RotorMachine/Rotor), which
45
+ perform the transposition ciphering and also rotate to produce a polyalphabetic (rather
46
46
  than simple substitution) cipher.
47
-
48
- * A [reflector](http://www.rubydoc.info/github/tammycravit/rotor_machine/master/RotorMachine/Reflector), which
47
+
48
+ * A [reflector](http://www.rubydoc.info/github/tammycravit/rotor_machine/master/RotorMachine/Reflector), which
49
49
  performs a simple symmetric substitution of letters
50
-
51
- * A [plugboard](http://www.rubydoc.info/github/tammycravit/rotor_machine/master/RotorMachine/Plugboard), which
50
+
51
+ * A [plugboard](http://www.rubydoc.info/github/tammycravit/rotor_machine/master/RotorMachine/Plugboard), which
52
52
  allows pairs of letters to be transposed on a per-message basis.
53
-
54
- On an actual Enigma machine, these components are all electromechanical, and
55
- the Enigma also included a keyboard, a grid of lights to show the results, and
56
- in some cases a printer. Since this is a simulated Enigma, obviously, no
53
+
54
+ On an actual Enigma machine, these components are all electromechanical, and
55
+ the Enigma also included a keyboard, a grid of lights to show the results, and
56
+ in some cases a printer. Since this is a simulated Enigma, obviously, no
57
57
  keyboard/printer are supplied here. In this simulation, the
58
58
  [Machine](http://www.rubydoc.info/github/tammycravit/rotor_machine/master/RotorMachine/Machine)
59
59
  class serves to encapsulate all of these components.
60
-
60
+
61
61
  The polyalphabetic encryption of the Enigma comes from the fact that the
62
62
  rotors are linked (mechanically in a real Enigma) so that they rotate
63
63
  one or more "steps" after each character, changing the signal paths and
64
64
  transpositions. This means that a sequence of the same plaintext character
65
65
  will encipher to different ciphertext characters.
66
-
66
+
67
67
  The rotors are designed to advance such that each time a rotor completes
68
68
  a full revolution, it will advance the rotor to its left once. The rotors
69
69
  allow you to configure how many positions they advance when they do. So,
70
70
  assuming all rotors are advancing one position at a time, if the rotors
71
71
  have position "AAZ", their state after the next character is typed will
72
72
  be "ABA".
73
-
73
+
74
74
  To learn much more about the inner workings of actual Enigma machines,
75
75
  visit [Enigma Machine (Wikipedia)](https://en.wikipedia.org/wiki/Enigma_machine).
76
-
76
+
77
77
  ### The Signal Path of Letters
78
-
78
+
79
79
  Here's a visual depiction of the signal path of a single character through
80
80
  a (physical) Enigma machine:
81
81
 
82
82
  ![Enigma signal path](https://github.com/tammycravit/rotor_machine/blob/master/images/File:Enigma_wiring_kleur.png?raw=1)
83
83
 
84
- As you can see, the electrical signal from a keypress is routed through the
85
- plugboard, then through each of the rotors in sequence from left to right.
86
- The signal then passes through the reflector (where it is transposed again),
87
- then back through the rotors in reverse order, and finally back through the
88
- plugboard a second time before being displayed on the light grid and/or
84
+ As you can see, the electrical signal from a keypress is routed through the
85
+ plugboard, then through each of the rotors in sequence from left to right.
86
+ The signal then passes through the reflector (where it is transposed again),
87
+ then back through the rotors in reverse order, and finally back through the
88
+ plugboard a second time before being displayed on the light grid and/or
89
89
  printer.
90
-
90
+
91
91
  The result of the machine's signal path being a loop is that encryption and
92
92
  decryption are the same operation. That is to say, if you set the rotors
93
93
  and plugboard, and then type your plaintext into the machine, you'll get
94
94
  a string of ciphertext. If you then reset the machine to its initial state
95
95
  and type the ciphertext characters into the machine, you'll produce your
96
96
  original plaintext.
97
-
98
- One consequence of the Enigma's design is that a plaintext letter will never
99
- encipher to itself. The Allies were able to exploit this property to help
100
- [break the Enigma's encryption](https://en.wikipedia.org/wiki/Cryptanalysis_of_the_Enigma)
97
+
98
+ One consequence of the Enigma's design is that a plaintext letter will never
99
+ encipher to itself. The Allies were able to exploit this property to help
100
+ [break the Enigma's encryption](https://en.wikipedia.org/wiki/Cryptanalysis_of_the_Enigma)
101
101
  during World War II.
102
-
102
+
103
103
  ## Usage
104
104
 
105
105
  To use the RotorMachine Enigma machine, you need to perform the following
106
106
  steps:
107
-
107
+
108
108
  1. Create a new `RotorMachine::Machine` object.
109
109
  2. Add one or more `RotorMachine::Rotor`s to the `rotors` array.
110
110
  3. Set the `reflector` to an instance of the `RotorMachine::Reflector` class.
111
111
  4. Make any desired connections in the Plugboard.
112
112
  5. Optionally, set the rotor positions with `#set_rotors`.
113
-
113
+
114
114
  You're now ready to encipher and decipher your text using the `#encipher`
115
115
  method to encode/decode, and `#set_rotors` to reset the machine state.
116
-
116
+
117
117
  The `#default_machine` and `#empty_machine` class methods are shortcut
118
- factory methods whcih set up, respectively, a fully configured machine
118
+ factory methods whcih set up, respectively, a fully configured machine
119
119
  with a default set of rotors and reflector, and an empty machine with
120
120
  no rotors or reflector.
121
121
 
@@ -134,7 +134,7 @@ machine.rotors << RotorMachine::Rotor.new(RotorMachine::Rotor::ROTOR_II, "A", 1)
134
134
  machine.rotors << RotorMachine::Rotor.new(RotorMachine::Rotor::ROTOR_III, "A", 1)
135
135
  machine.reflector = RotorMachine::Reflector.new(RotorMachine::Reflector::REFLECTOR_A)
136
136
 
137
- machine.plugboard.connect("A", "M")
137
+ machine.plugboard.connect("A", "M")
138
138
  machine.plugboard.connect("Q", "K")
139
139
 
140
140
  machine.set_rotors("CFL")
@@ -145,7 +145,7 @@ machine.set_rotors("CFL")
145
145
  new_plaintext = machine.encipher(ciphertext) # => "THISI SASUP ERSEC RETME SSAGE"
146
146
  ```
147
147
 
148
- ## Example - Simplified Setup Using the Factory
148
+ ## Example - Simplified Setup Using the Factory
149
149
 
150
150
  ```ruby
151
151
  require 'rotor_machine'
@@ -164,58 +164,83 @@ machine.set_rotors("CFL")
164
164
  new_plaintext = machine.encipher(ciphertext) # => "THISI SASUP ERSEC RETME SSAGE"
165
165
  ```
166
166
 
167
+ ## Using the Wrapper DSL
168
+
169
+ A simple wrapper DSL (domain-specific language) is provided, primarily for
170
+ testing and other "conversational" or interactive uses. This DSL is defined in
171
+ the `RotorMachine::Session` class. Usage is similar to the following:
172
+
173
+ ```ruby
174
+ RotorMachine.Session do
175
+ default_machine
176
+
177
+ set_rotors "AAA"
178
+ connect "A", "G"
179
+ encipher "THIS IS A SUPER SECRET MESSAGE"
180
+ ct = last_result
181
+
182
+ set_rotors "AAA"
183
+ encipher ct
184
+ puts last_result # THISI SASUP ERSEC RETME SSAGE
185
+ end
186
+ ```
187
+
188
+ After the operations in the block are executed, the `RotorMachine.Session` method
189
+ will return the `RotorMachine::Session` object, which can be further reused if
190
+ needed.
191
+
167
192
  ## Documentation
168
193
 
169
- The classes in
170
- [`lib/rotor_machine/`](https://github.com/tammycravit/rotor_machine/tree/master/lib/rotor_machine)
171
- all contain [documentation](http://www.rubydoc.info/github/tammycravit/rotor_machine/master/) that
172
- pretty exhaustively describe their operation.
173
- The RSpec tests in the [`spec/`](https://github.com/tammycravit/rotor_machine/tree/master/spec)
194
+ The classes in
195
+ [`lib/rotor_machine/`](https://github.com/tammycravit/rotor_machine/tree/master/lib/rotor_machine)
196
+ all contain [documentation](http://www.rubydoc.info/github/tammycravit/rotor_machine/master/) that
197
+ pretty exhaustively describe their operation.
198
+ The RSpec tests in the [`spec/`](https://github.com/tammycravit/rotor_machine/tree/master/spec)
174
199
  directory are also instructive for how the library works and how to use it.
175
200
 
176
201
  ## Development
177
202
 
178
- After checking out the repo, run `bin/setup` to install dependencies. Then,
179
- run `rake spec` to run the tests. You can also run `bin/console` for an
203
+ After checking out the repo, run `bin/setup` to install dependencies. Then,
204
+ run `rake spec` to run the tests. You can also run `bin/console` for an
180
205
  interactive prompt that will allow you to experiment.
181
206
 
182
- To install this gem onto your local machine, run `bundle exec rake install`.
207
+ To install this gem onto your local machine, run `bundle exec rake install`.
183
208
 
184
- To release a new version, update the version number in `version.rb`, and then
185
- run `bundle exec rake release`, which will create a git tag for the version,
209
+ To release a new version, update the version number in `version.rb`, and then
210
+ run `bundle exec rake release`, which will create a git tag for the version,
186
211
  push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
187
212
 
188
- This gem depends on the [`tcravit_ruby_lib`](https://github.com/tammycravit/tcravit_ruby_lib)
189
- gem, which provides Rake tasks to update the version number. You can use the
190
- `bundle exec rake version:bump:build`, `bundle exec version:bump:minor` and
191
- `bundle exec rake version:bump:major` tasks to increment the parts of
213
+ This gem depends on the [`tcravit_ruby_lib`](https://github.com/tammycravit/tcravit_ruby_lib)
214
+ gem, which provides Rake tasks to update the version number. You can use the
215
+ `bundle exec rake version:bump:build`, `bundle exec version:bump:minor` and
216
+ `bundle exec rake version:bump:major` tasks to increment the parts of
192
217
  the version number. (These tasks rewrite the file
193
- [`lib/rotor_machine/version.rb`](https://github.com/tammycravit/rotor_machine/blob/master/lib/rotor_machine/version.rb).
194
- After using them, you'll need to run a `git add lib/rotor_machine/version.rb`
218
+ [`lib/rotor_machine/version.rb`](https://github.com/tammycravit/rotor_machine/blob/master/lib/rotor_machine/version.rb).
219
+ After using them, you'll need to run a `git add lib/rotor_machine/version.rb`
195
220
  and `git commit -m "version bump"`.
196
221
 
197
222
  ### Contributing
198
223
 
199
- Bug reports and pull requests are welcome on GitHub at
200
- [https://github.com/tammycravit/rotor_machine]. Pull requests for code changes
224
+ Bug reports and pull requests are welcome on GitHub at
225
+ [https://github.com/tammycravit/rotor_machine]. Pull requests for code changes
201
226
  should include [RSpec](http://rspec.info) tests for the new/changed features.
202
227
  Pull requests for documentation and other updates are also welcome.
203
228
 
204
- This project is intended to be a safe, welcoming space for collaboration, and
205
- contributors are expected to adhere to the
229
+ This project is intended to be a safe, welcoming space for collaboration, and
230
+ contributors are expected to adhere to the
206
231
  [Contributor Covenant](http://contributor-covenant.org) code of conduct.
207
- Contributions from people who identify as women, BIPOC folx, LGBT folx,
232
+ Contributions from people who identify as women, BIPOC folx, LGBT folx,
208
233
  and members of other marginalized communities are especially welcomed.
209
234
 
210
235
  ### Code of Conduct
211
236
 
212
- Everyone interacting in the RotorMachine project’s codebases, issue trackers,
213
- chat rooms and mailing lists is expected to follow the
237
+ Everyone interacting in the RotorMachine project’s codebases, issue trackers,
238
+ chat rooms and mailing lists is expected to follow the
214
239
  [code of conduct](https://github.com/tammycravit/rotor_machine/blob/master/CODE_OF_CONDUCT.md).
215
240
 
216
241
  ## License
217
242
 
218
- The gem is available as open source under the terms of the
243
+ The gem is available as open source under the terms of the
219
244
  [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) license.
220
245
 
221
246
  ## Image Credits
@@ -223,6 +248,6 @@ The gem is available as open source under the terms of the
223
248
  * Enigma image - from [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Bundesarchiv_Bild_183-2007-0705-502,_Chiffriermaschine_%22Enigma%22.jpg),
224
249
  provided by Das Bundesarchiv (German Federal Archives).
225
250
 
226
- * Enigma signal path image - from [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Enigma_wiring_kleur.svg),
251
+ * Enigma signal path image - from [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Enigma_wiring_kleur.svg),
227
252
  by [MesserWoland](https://commons.wikimedia.org/wiki/User:MesserWoland)
228
253
 
@@ -0,0 +1,161 @@
1
+ module RotorMachine
2
+ ##
3
+ # The {Session} object provides a very simple DSL for "conversational" interactions
4
+ # with the {RotorMachine::Machine} and its subordinate classes. This is useful for
5
+ # interactive and experimental applications, for testing, and so forth.
6
+ #
7
+ # Eventually, a {Pry}-based REPL loop might be added to the project, but this
8
+ # functionality does not exist yet.
9
+ #
10
+ # Instance methods are a loose wrapper around the rest of the library and are only
11
+ # loosely documented here. Argument validation is handled by simply bubbling up
12
+ # exceptions raised by the implementatation method.
13
+ #
14
+ # == Example Usage
15
+ #
16
+ # RotorMachine.Session do
17
+ # default_machine
18
+ #
19
+ # set_rotors "AAA"
20
+ # connect "A", "G"
21
+ # encipher "THIS IS A SUPER SECRET MESSAGE"
22
+ # ct = last_result
23
+ #
24
+ # set_rotors "AAA"
25
+ # encipher ct
26
+ # puts last_result # THISI SASUP ERSEC RETME SSAGE
27
+ # end
28
+ class Session
29
+ ##
30
+ # Initialize the {RotorMachine::Session} instance. The methods of this object,
31
+ # except for {#machine} and {#last_result}, are primarily intended to be
32
+ # called within the {Session} block (via {instance_eval}).
33
+ #
34
+ # @param opts [Hash] The setup options hash. Currently unused, but any options
35
+ # provided are stored.
36
+ # @param block [Block] The operations block. If provided, it is executed via
37
+ # {instance_eval}. The instance is returned following
38
+ # execution of the block.
39
+ def initialize(opts={}, &block)
40
+ @opts = opts
41
+ @machine = RotorMachine::Factory.empty_machine()
42
+ @last_result = nil
43
+ instance_eval(&block) if block_given?
44
+ return self
45
+ end
46
+
47
+ ##
48
+ # Create a rotor and add it to the machine.
49
+ def rotor(kind, position=0, step_size=1)
50
+ r = RotorMachine::Factory.build_rotor rotor_kind: kind,
51
+ initial_position: position,
52
+ step_size: step_size
53
+ @machine.rotors << r if r.is_a?(RotorMachine::Rotor)
54
+ end
55
+
56
+ ##
57
+ # Set the machine's reflector.
58
+ def reflector(kind, position="A")
59
+ r = RotorMachine::Factory.build_reflector reflector_kind: kind,
60
+ initial_position: position
61
+ @machine.reflector = r if r.is_a?(RotorMachine::Reflector)
62
+ end
63
+
64
+ ##
65
+ # Connect a pair of letters on the machine's plugboard.
66
+ def connect(from, to)
67
+ @machine.plugboard.connect(from, to)
68
+ end
69
+
70
+ ##
71
+ # Disconnect a letter (and its inverse) from the machine's plugboard.
72
+ def disconnect(from)
73
+ @machine.plugboard.disconnect(from)
74
+ end
75
+
76
+ ##
77
+ # Encipher a string.
78
+ def encipher(the_string="")
79
+ res = @machine.encipher(the_string)
80
+ @last_result = res
81
+ res
82
+ end
83
+
84
+ ##
85
+ #Set the positions of the rotors.
86
+ def set_positions(pos_string)
87
+ @machine.set_rotors(pos_string)
88
+ end
89
+
90
+ ##
91
+ # Remove all rotors from the machine.
92
+ def clear_rotors
93
+ @machine.rotors = []
94
+ end
95
+
96
+ ##
97
+ # Remove all connections from the plugboard.
98
+ def clear_plugboard
99
+ @machine.plugboard = RotorMachine::Plugboard.new
100
+ end
101
+
102
+ ##
103
+ # Configure the machine to its default state (as in the {RotorMachine::Factory}
104
+ # object's {default_machine} method.)
105
+ def default_machine
106
+ @machine = RotorMachine::Factory.default_machine
107
+ end
108
+
109
+ ##
110
+ # Configure the machine to its empty state (as in the {RotorMachine::Factory}
111
+ # object's {empty_machine} method.)
112
+ def empty_machine
113
+ @machine = RotorMachine::Factory.empty_machine
114
+ end
115
+
116
+ ##
117
+ # Return the inner {RotorMachine::Machine} object.
118
+ def machine
119
+ @machine
120
+ end
121
+
122
+ ##
123
+ # Return the results of the last {encipher} operation, or nil.
124
+ def last_result
125
+ @last_result
126
+ end
127
+
128
+ ##
129
+ # {plug} is a convenience alias for {connect}
130
+ alias_method "plug", "connect"
131
+
132
+ ##
133
+ # {unplug} is a convenience alias for {disconnect}
134
+ alias_method "unplug", "disconnect"
135
+
136
+ ##
137
+ # {set_rotors} is a convenience alias for {set_positions}
138
+ alias_method "set_rotors", "set_positions"
139
+
140
+ ##
141
+ # {encode} is a convenience alias for {encipher}
142
+ alias_method "encode", "encipher"
143
+
144
+ ##
145
+ # {cipher} is a convenience alias for {encipher}
146
+ alias_method "cipher", "encipher"
147
+
148
+ ##
149
+ # {the_machine} is a convenience alias for {machine}
150
+ alias_method "the_machine", "machine"
151
+ end
152
+
153
+ ##
154
+ # The class method Session is the entrypoint for the DSL. When invoked with a
155
+ # block, it creates a new {RotorMachine::Session} object, passes the block to
156
+ # it to be run with {instance_eval}, and then the {RotorMachine::Session} object
157
+ # is returned to the caller.
158
+ def self.Session(opts={}, &block)
159
+ RotorMachine::Session.new(opts, &block)
160
+ end
161
+ end
@@ -1,4 +1,4 @@
1
1
  module RotorMachine
2
- VERSION_DATA = [1, 1, 1]
2
+ VERSION_DATA = [1, 2, 0]
3
3
  VERSION = VERSION_DATA.join(".")
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rotor_machine
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tammy Cravit
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-06-22 00:00:00.000000000 Z
11
+ date: 2018-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tcravit_ruby_lib
@@ -195,6 +195,7 @@ files:
195
195
  - lib/rotor_machine/plugboard.rb
196
196
  - lib/rotor_machine/reflector.rb
197
197
  - lib/rotor_machine/rotor.rb
198
+ - lib/rotor_machine/session.rb
198
199
  - lib/rotor_machine/string_extensions.rb
199
200
  - lib/rotor_machine/version.rb
200
201
  - rotor_machine.gemspec