In this post we’re going to pick up our simple xorer application where we left off. We wanted to add the ability to read in a ‘key’ (string of characters) and a file containing ‘plain text’ from the command line. Then xor the ‘key’ against the ‘plain text’ and output the resulting ‘cipher text’ to a file. If you do not have the previous code, it’s hosted on github.
To start let’s reorganize our project a bit. Create a spec directory for our tests. Create a lib directory for our xorer module. Create a bin directory for our executable.
Now let’s separate our test code from our implementation code. Create a new file spec/xorer_spec.rb move the following code from xorer.rb into it:
Notice the require './lib/xorer' at the top of the file. This is important since our next step is to move xorer.rb to lib/xorer.rb. This tells rspec where to find our implementation code.
Let’s run our tests and make sure everything is still passing.
Great, with that clean up out of the way we can focus on adding more functionality to our application. I imagine running our application from the command line with the following syntax:
So the user must pass in the ‘key’ as the first argument, followed by the input or ‘plain text’ file, and lastly the output or ‘cipher text’ file.
Then to reverse the ‘cipher text’ the user would simply run:
So let’s create a test.txt file to work with:
We are piping in input from the terminal to a new file that we are naming test.txt all in one step. The ^D(press control+d) tells the terminal we want to stop inputing text and return to the the prompt.
Now let’s create our bin/xorer file. This will be a ruby file. To make it executable we need to do a few things.
Now add the shebang line to the very top of the file and output a simple Hello World!:
If we run it we should see the following output:
Now let’s make sure the user passes the right amount of arguments onto the command line. Remove the ‘hello world’ line, and add the following:
Now if you try to pass in the wrong arguments you will get an error message and the application with quit.
Let’s create a run method in our Xorer module that accepts the 3 necessary parameters. Open lib/xorer.rb and add the following:
In our bin/xorer file let’s add a call to the Xorer.run()
In order to work with the ‘key’ we need to convert the the string value to binary string.
We need to do the same thing for the ‘plain text’ file. Since in ruby a String and File both have the each_byte method available to them we do not have to change bytes_to_binary_array. We just need to get a file handle and pass that into the bytes_to_binary_array method.
I am not going to write a test for bytes_to_binary_array this since we are already testing the underlying method byte_to_binary_string which produces the binary string from the byte.
Here is where we run into our first problem. Our ‘key’ needs to be the same amount of characters as our ‘plain text’. If they are not the same length we will not be able to xor the characters correctly. If we try to xor this it will result in an error similar to the following:
So let’s make sure the ‘key’ is at least the same length. If it is not the remaining characters of the ‘key’ should just be ignored.
Do you have a test for that?
Does the test pass?
Write just enough code for the test to pass
Does the test pass?
The next issue we will face is the possibility that the binary arrays will not be the same size. Take for instance the character T and . from our test.txt file. T becomes ... and . becomes .... If we try to xor this it will result in an error similar to the following:
So we need to pad the binary array with 0’s. First we need to find out the length of the largest binary array.
Do you have a test for that?
Write just enough code for the test to pass
Does the test pass?
Next we pad with zeros:
Do you have a test for that?
Write just enough code for the test to pass
Does the test pass?
Now that all our tests are passing and we have all of the functionality in place, it’s time to put the finishing touch on the application. Let’s open a file to write to, and xor each element in the binary array, then convert the binary string to a byte, and convert the byte to a character, and lastly write the value to a file.
Let’s see the fruits of our labor:
So as you can see the cipher text looks nothing like our plain text. I am going to stop here. I had initially wanted to refactor some of the methods by using the ruby built in pack and unpack methods. Here are a few examples of how to use unpack:
Check the ruby documentation and if you feel like experimenting a little further, you can refactor some of the methods we’ve created to use pack and unpack instead of our implementation. Also take a look at benchmarking in ruby and benchmark any changes you make.
Ruby provides methods to benchmark and report the time used to execute Ruby code.
Hope this was somewhat helpful, and if you have any comments or feedback please let me know. You can view the source code hosted on github.