Solving the CauzCoin Retro BattleStations Challenge

Context

Chris Osborn (@FozzTexx) runs an awesome subreddit called Retro BattleStations. If you like old computers you should check it out.

Today I discovered he posted a new Retro BattleStations Challenge. He put some money in a Bitcoin wallet, obfuscated and then encoded the wallet's private key in a Cauzin Softstrip, published it on the web, and was waiting to see who would claim the contents first. Softstrip is a machine-readable optical encoding for computer files. It was used by computer magazines to distribute programs on paper alongside human-readable articles in the 1980s. A Bitcoin wallet's private key is the secret you need to spend the contents of the wallet.

It took about two hours to solve the challenge, and about the same amount of time to write up this solution. I used Debian GNU/Linux and Python. Here's my solution to the challenge.

Step 1: Download the PDF file

$ wget http://www.insentricity.com/file.cl/11742.pdf

Result is 11742.pdf

Step 2: Convert PDF to PNG

I used Ghostscript for this.

$ gs -q -dNOPAUSE -dBATCH -r600 -sDEVICE=pnggray -sOutputFile=11742.png 11742.pdf

Result is 11742.png

Step 3: Isolate just the barcode

I used GIMP to crop out just the Cauzin softstrip barcode. Select the approximate region required, crop, then use autocrop to trim it exactly.

Result is 11742-cropped.png

Step 4: Convert the image into text

Text is much easier to manipulate, so I converted the matrix from graphical to text representation. My python script "image-decoder" does this task.

The wise boy would write a program which located the strip in a larger image, figured out the dimensions of each element of the image, etc. The lazy boy has only one strip he wants to decode so those parameters can just be guessed and hard coded! The Python script outputs the text-format version of the strip on stdout and also shows an image with a red dot in the center of where it thinks each pixel is so you can visually check it is sampling the correct locations.

$ ./image-decoder 11742-cropped.png > 11742-raw.txt

Result is 11742-raw.txt, and you can see which pixels it sampled here (you may need to zoom in to see the red dots).

Note that this step would have been much harder if the barcode had been scanned in from paper, which would have introduced distortions and imperfect transformations. The fact that it came from a machine-generated PDF means the geometry and constrast was perfectly maintained and made locating the center of each pixel in the barcode much easier.

Step 5: Extract the payload

We need to decode the Cauzin strip to extract the payload. The python script "strip-decoder" does this task. Again since I have only one image to decode and was in a rush it is somewhat specific to the format of this single strip. It does check the left and right alignment stripes, the zipper and checkerboard, and the parity of the recovered data, and it can (in principle!) handle multiple files in a single strip.

$ ./strip-decoder 11742-raw.txt
Decoded payload has strip ID 'TICKET', length 686 bytes, 1 file(s)
File name 'code2.txt.bz2', length 653 bytes

On the first pass through this challenge I didn't parse the Cauzin headers, instead I just parsed out the length and dumped that many bytes from the strip into a file. I then looked at a hex dump of the result, saw what looked like a filename ("code2.txt.bz2") shortly followed by a sequence that I recognised as a BZIP2 header ("BZh"), so I stripped off everything before that header and moved on to the next step (decompression). I then got stuck on the following step and, thinking maybe the 33 bytes I had skipped might contain a clue, came back and improved the decoder to parse this header properly. (No extra clues were found!)

Result is code2.txt.bz2

Step 6: Decompress the file

$ bunzip2 code2.txt.bz2

A trivial step. Result is "code2.txt".

Step 7: Interpret the file

This one had me stumped for a while. If you've not had a look yet, the file is a single line of text 2,382 bytes in length. The first few bytes look like this:

63T542M-63T-33T108MD123T44M63T33M-269T67M233T67MU-206T74M90T90MD

I observed it was a sequence of positive and negative integers interspersed with the letters M, T, U and D. M and T were always preceeded by an integer, D and U are always preceded by an M or T.

I decided that probably the letters were commands and the numbers were arguments to those commands. I know Chris owns a vinyl cutter which is a bit like a plotter, so I thought maybe these were commands to an XY plotter, with M and T corresponding to X and Y motion ("Move" and "Translate"?) and D and U corresponding to lowering and raising the pen ("Down" and "Up"?). A little Python later I had this (I used red and blue lines to indicate movement when the pen was up or down):

So that was pretty obviously wrong, not sure how I can extract a Bitcoin private key from that squiggle. The hypothetical encoding also seemed daft since it could only move parallel to the X or Y axes, so drawing a diagonal line would require many commands each doing little steps alternating between X and Y. At this stage I went back and made improvements to the Cauzin softstrip decoder hoping to find a clue (it wasn't there)

I noticed that the plot moved from left to right, never reversing, so the arguments to the M commands are always positive. A little tweak to the Python script showed that the M arguments were in the range 0 to 991 and the T arguments were in the range -306 to 270. That second range was the clue I needed, seems very much like we're encoding angles in degrees, so perhaps the encoding is using polar co-ordinates to describe how to move a turtle. M is for Move forward, T is for Turn.

A quick update to my Python script, "craplot", and we get some sensible output at last:

$ ./craplot code2.txt privatekey.png

Result is "privatekey.png":


Click here to see a version including movements with the pen lifted

The wonkiness is presumably the result of accumulated rounding errors.

Step 8: Make off with the loot

I've not used Bitcoin before, although I understand the principles behind it. A little Googling revealed that private keys are encoded in 51 characters and start with "5", which matches what was revealed.

A few minutes later I had installed the Electrum bitcoin wallet, created a new wallet, transcribed and imported the challenge private key (it was "5KPzaKE1UMTRFRHQUwVpY8Ku2oXQemfQ5sZKE6akBnsuHTk3t4s"), and transferred out the winnings.

--

I have now donated the proceeds to the Royal National Lifeboat Institution, who accept bitcoin directly.

Many thanks to FozzTexx, I really love taking part in his challenges!! If you like this type of thing you may also enjoy his previous April BBS challenge which is still fun to solve (although the prize for completing it is no longer available).

Will Sowerbutts, 2016-09-01