diff --git a/DemoMultiThreaded.rb b/DemoMultiThreaded.rb new file mode 100644 index 0000000..7187a85 --- /dev/null +++ b/DemoMultiThreaded.rb @@ -0,0 +1,117 @@ +## +# Demo.rb +# Created: February 10, 2013 +# By: Ron Bowes +# +# A demo of how to use Poracle, that works against RemoteTestServer. +## +# +# coding: utf-8 +require 'httparty' +require './Poracle' +require './Utilities' +require 'optparse' +require 'thread/pool' +require 'ostruct' + +class Demo + attr_reader :iv, :data, :blocksize,:newlinechars,:url,:starting_block + NAME = "Demo" + # This function should load @data, @iv, and @blocksize appropriately + def initialize() + @data = HTTParty.get("http://localhost:20222/encrypt").parsed_response + # Parse 'data' here + #@data = [@data].pack("H*") + @data = [@data].pack("H*").unpack('C*') + @iv = nil + @blocksize = 16 + end + + # This should make a decryption attempt and return true/false + def attempt_decrypt(data) + data= data.flatten.pack('C*').unpack("H*") + result = HTTParty.get("http://localhost:20222/decrypt/#{data.join}").parsed_response + # Match 'result' appropriately + return result !~ /Fail/ + end + + # Optionally define a character set, with the most common characters first + def character_set() + charset=' eationsrlhdcumpfgybw.k:v-/,CT0SA;B#G2xI1PFWE)3(*M\'!LRDHN_"9UO54Vj87q$K6zJY%?Z+=@QX&|[]<>^{}' + return charset.chars.to_a + end +end + +#Main Program +options = Utilities.parse(ARGV) +verbose = options.verbose +file = options.file +skip_blocks=[] +sortfile =options.sortfile +threadsize =options.threadsize + +if (sortfile) + Utilities.sort_sessionfile(file) + results= Utilities.parse_sessionfile(file) + puts "DECRYPTED: " + results.join + + exit +end + +# Read already decrypted block from last time and add them to skip_blocks +results= Utilities.parse_sessionfile(file) +if (!results.nil?) + results.each_with_index do |val,i| + if (!val.nil? and val!="\n") + skip_blocks< Starting poracle decrypter with module #{mod.class::NAME}") +puts(">> Encrypted length: %d" % mod.data.length) +puts(">> Blocksize: %d" % mod.blocksize) +puts(">> %d blocks" % blockcount) +iv = "\x00" * mod.blocksize + +if (verbose) + i=0 + blocks= mod.data.each_slice(mod.blocksize).to_a + blocks.each do |b| + i = i + 1 + puts(">>> Block #{i}: #{b.pack("C*").unpack("H*")}") + end +end + +start = Time.now + +# Specify pool size +pool = Thread.pool(threadsize) +blockcount=mod.data.length / mod.blocksize +results=Array.new(blockcount) + +# Spawn new thread for each block +(blockcount ).step(1,-1) do |i| + if (!skip_blocks.include?(i)) + pool.process { + result=Poracle.decrypt(mod, mod.data, iv, verbose, i, file) + results[i]=result.pack('C*').force_encoding('utf-8') + } + end +end +pool.shutdown +puts "DECRYPTED: " + results.join +finish= Time.now +Utilities.sort_sessionfile(file) +puts sprintf("DURATION: %0.02f seconds", (finish - start) % 60) \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..6ed3893 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +gem 'thread', '0.1.3' +gem 'httparty', '0.12.0' diff --git a/Poracle.rb b/Poracle.rb index 978f662..73e8a60 100644 --- a/Poracle.rb +++ b/Poracle.rb @@ -44,42 +44,45 @@ def Poracle.ord(c) end return c.unpack('C')[0] end - + def Poracle.generate_set(base_list) mapping = [] + new_list=[] base_list.each do |i| + new_list<< ord(i) mapping[ord(i)] = true end - 0.upto(255) do |i| if(!mapping[i]) - base_list << i.chr + new_list <^{}'.chars.to_a) end - # Break the current character (this is the secret sauce) - c = find_character(mod, character, block, previous, plaintext, set, verbose) - plaintext[character] = c - + c = find_character(mod, character, blocks, i, plaintext, set, verbose) + plaintext[character] = c.nil?? 0:c + if (c.nil?) + puts "Skipping block #{i}" + break + end + count+=1 if(verbose) - puts(plaintext) + puts "#{i} --> #{plaintext}" end - end + if (count==mod.blocksize) + File.open(file, 'a') do |f| + f.puts "#{i},#{plaintext.pack('C*').force_encoding('utf-8')}" + end + end + end return plaintext end # This is the public interface. Call this with the mod, data, and optionally # the iv, and it'll return the decrypted text or throw an error if it can't. # If no IV is given, it's assumed to be NULL (all zeroes). - def Poracle.decrypt(mod, data, iv = nil, verbose = false) + def Poracle.decrypt(mod, data, iv = nil, verbose = true, start = data.length / mod.blocksize, file) # Default to a nil IV - if(iv.nil?) - iv = "\x00" * mod.blocksize + + if(!iv.nil?) + iv =iv.unpack('C*') + data = iv + data end - # Add the IV to the start of the encrypted string (for simplicity) - data = iv + data + + blockcount = data.length / mod.blocksize - # Validate the blocksize - if(data.length % mod.blocksize != 0) - puts("Encrypted data isn't a multiple of the blocksize! Is this a block cipher?") - end - - # Tell the user what's going on - if(verbose) - puts("> Starting Poracle decrypter with module #{mod.class::NAME}") - puts(">> Encrypted length: %d" % data.length) - puts(">> Blocksize: %d" % mod.blocksize) - puts(">> %d blocks:" % blockcount) - end - # Split the data into blocks - using unpack is kinda weird, but it's the # best way I could find that isn't Ruby 1.9-specific - blocks = data.unpack("a#{mod.blocksize}" * blockcount) + blocks = data.each_slice(mod.blocksize).to_a i = 0 blocks.each do |b| i = i + 1 - if(verbose) - puts(">>> Block #{i}: #{b.unpack("H*")}") - end end # Decrypt all the blocks - from the last to the first (after the IV). # This can actually be done in any order. - result = '' - is_last_block = true - (blocks.size - 1).step(1, -1) do |i| + result = Array.new + is_last_block = (start==blockcount-1 ? true:false) + i=start # Process this block - this is where the magic happens - new_result = do_block(mod, blocks[i], blocks[i - 1], is_last_block, verbose) + new_result = do_block(mod, blocks, i, is_last_block, verbose, file) if(new_result.nil?) return nil end is_last_block = false result = new_result + result - if(verbose) - puts(" --> #{result}") - end - end - # Validate and remove the padding - pad_bytes = result[result.length - 1].chr + + pad_bytes = result[result.length - 1] if(result[result.length - ord(pad_bytes), result.length - 1] != pad_bytes * ord(pad_bytes)) - puts("Bad padding:") - puts(result.unpack("H*")) - return nil + return result end # Remove the padding result = result[0, result.length - ord(pad_bytes)] - return result end -end + +end \ No newline at end of file diff --git a/Utilities.rb b/Utilities.rb new file mode 100644 index 0000000..25a8485 --- /dev/null +++ b/Utilities.rb @@ -0,0 +1,80 @@ +class Utilities + def self.parse(args) + # The options specified on the command line will be collected in *options*. + # We set default values here. + options = OpenStruct.new + options.file = "" + options.threadsize = 1 + options.sortfile = false + options.verbose = false + + opt_parser = OptionParser.new do |opts| + opts.banner = "Usage: Demo.rb [options]" + opts.separator "" + opts.separator "Specific options:" + + # Sort temprary results + opts.on("-s", "--sort", "Sort temporary results") do |s| + options.sortfile = s + end + + # Verbose + opts.on("-v", "--verbose", "Show debug messages") do |v| + options.verbose = v + end + + # Threadpool size + opts.on("-t", "--threads SIZE", "Set threadpool size") do |size| + options.threadsize = Integer(size) + end + + # Save to file + opts.on("-f", "--file FILE","Save temporary results to file") do |file| + options.file = file + end + + opts.on_tail("-h", "--help", "Show this message") do + puts opts + exit + end + end + opt_parser.parse!(args) + options + end + + def self.parse_sessionfile(file) + results=[] + begin + text=File.open(file).read + text.gsub!(/\r\n?/, "\n") + text.each_line do |line| + results[Integer(line.split(',',2)[0])]=line.split(',',2)[1].gsub("\n","") + end + return results + end + rescue + File.open(file, 'w') do |f| + end + end + + def self.sort_sessionfile(file) + results=[] + text=File.open(file).read + text.gsub!(/\r\n?/, "\n") + text.each_line do |line| + results[Integer(line.split(',',2)[0])]=line.split(',',2)[1] + end + File.open(file, 'w') do |f| + end + results.each_with_index do |result,i| + File.open(file, 'a') do |f| + if (result.nil?) + f << "#{i},\n" + else + f << "#{i},#{result}" + end + end + end + end + +end \ No newline at end of file