-- -stevan apter" <apter / panix.com> writes: > i was hoping that my previous post on the solitaire algorithm > would stimulate an attack on the problem Well, in the interests of putting a stake in the ground, below is my simplistic attempt at the Solitaire encrypt/decrypt program. I didn't do anything clever-I just implemented it from the recipe. I suspect the interesting part will come from two sources: 1. Optimizing a solution: the code below is pretty much stream-of-consciousness stuff. There will be plenty of more optimal ways of doing things. In particular, I don't think it's necessary to do all the copying of the deck during cutting. 2. Going terse. Again, I wrote the code below for simplicity, not terseness. I suspect Ruby's more functional style of programming would lead to some very compact solutions. Let's see it folks - a one-liner please! Anyway, be gentle on me - I've had a long couple of days... Regards Dave (ps. The official Perl code posted on counterpane.com has a bug in decryption, as I found out when doing comparative testing) -- -Content-Disposition: attachment; filename=t1.rb Content-Description: Solitaire program #!/usr/bin/env ruby ############################################################# # # Quick'n'nasty Ruby implementation of the Solitaire # cryptosystem. # # Dave Thomas <dave / thomases.com> May 2000 # ############################################################# # # We represent the deck internally as the binary bytes 1..54, # with 53 being the A Joker and 54 the B joker. # # The implementation is pretty straightforward. The Deck is created # and manipulated by the KeyStream class, and encryption and decryption # by CodeBook. # ################################################################## # # It's convenient to have some additional functionality for Strings class String # Return a normalized string given arbitrary input # Convert lowecase to uppercase, and remove non-alphas def normalize upcase.scan(/[A-Z]+/).join('') end # pad a string to a multiple of five characters def pad return (self + "XXXX")[0...5*((length+4)/5)] end # Return a new string with a space every 5 characters def byFives scan(/...../).join(' ') end end ################################################################## # # This is the deck of cards class KeyStream A 3; AC .chr B 4; BC .chr # Print a readable form of the Deck for debugging def printDeck(deck eck) res deck.tr("\001-\066", 'A-Za-z@*') puts res end # Initialized ourselves with the given passphrase. The encrypt # parameter is true if we're an encrypter, false if we're decrypting # We initialize the deck from the passphrase by shuffling and cutting # for each character the passphrae contains. def initialize(passphrase, encrypt) @encrypt ncrypt ? 1 : -1 @deck 1..54).to_a.pack('c*') passphrase.each_byte do |ch| shuffle countCut(ch - ?A + 1) end end # Perform one pass through the deck. Move the A joker down one, the # B joker down two, triple cut around the jokers, and then count cut # based on the last character in the deck. def shuffle moveDown(A) moveDown(B) moveDown(B) tripleCut countCut end # Move the given character down, wrapping around the end of the # deck if necessary def moveDown(char) if @deck[53] char @deck deck[0,1] + char.chr + @deck[1..52] else @deck.sub!(/(#{char.chr})(.)/m) { "#$2#$1" } end end # swap the segments that are before the first joker and after the last def tripleCut @deck.sub!(/(.*)([#{AC}#{BC}].*[#{AC}#{BC}])(.*)/m) { "#$3#$2#$1" } end # Swap the segments before and after the 'withChar'th chapter # leaving the last character untouched def countCut(withChar deck[53]) withChar if withChar B @deck[0..52] deck[withChar..52] + @deck[0...withChar] end # Generate the next key, ignoring jokers def nextKey loop { shuffle offset deck[0] offset if offset B return @encrypt * @deck[offset] if @deck[offset] < A } end end ################################################################## # # This simple class controls encryption # class CodeBook def initialize(passphrase, encrypt rue) @keystream eyStream.new(passphrase.normalize, encrypt) end # encrypt/decrypt a string def encrypt(cleartext) text leartext.normalize.pad for i in 0...text.length text[i] text[i] - ?A + @keystream.nextKey)%26 + ?A end text end end ################################################################## # # Test program. Read alternate lines of passphrase/cleartext/expected. # Create an encryptor, encrypt the cleartext, then create # a decryptor and decrypt it again # while passphrase ets enc odeBook.new(passphrase) coded nc.encrypt(gets) puts coded.byFives expected ets.chomp raise "Failed: '#{coded} ! {expected}" unless coded expected dec odeBook.new(passphrase, false) decoded ec.encrypt(coded) puts decoded.byFives puts "------------------------------------------------------------" end ################################################################## __END__ AAAAA AAAAA EXKYIZSGEH FOO AAAAA AAAAA AAAAA ITHZUJIWGRFARMW CRYPTONOMICON SOLITAIRE KIRAKSFJAN -- -