Permalink
Browse files

Adding code for dojo 3

  • Loading branch information...
1 parent bae580e commit 521612729176948849b47ef7f5bf06f034d5b4e4 @jamesnvc jamesnvc committed Feb 4, 2012
@@ -22,6 +22,7 @@ def index
# GET /contests/1.xml
def show
@contest = Contest.find(params[:id])
+ @num_probs = @contest.puzzle_ident == 3 ? 3 : 2
if @contest.start > DateTime.now
unless current_user.is_admin
redirect_to contests_path, alert: "That contest has not yet started"
@@ -44,6 +45,8 @@ def problem
return
end
+ @max_time_allowed = 120
+
# To add more dojos, add another elsif contest_ident == statement. In the
# block, simply add another elsif block for the appropriate contest_ident
# which sets the "args" variable to be the array of arguments which will be
@@ -61,8 +64,14 @@ def problem
elsif contest_ident == 2
unless (0..2).member? @level
redirect_to @contest, alert: "Invalid level"
- args = []
+ return
+ end
+ elsif contest_ident == 3
+ unless (0..3).member? @level
+ redirect_to @contest, alert: "Invalid level"
+ return
end
+ @max_time_allowed = @level <= 1 ? 120 : 300
else
redirect_to @contest, alert: "Invalid contest"
end
@@ -83,6 +92,15 @@ def problem
elsif @level > 0
msg = @prob[:searches].map{|x| x[0]}.join('+') + @prob[:locations].join('+')
end
+ elsif contest_ident == 3
+ aes = OpenSSL::Cipher::Cipher.new("AES-128-CBC")
+ aes.encrypt
+ aes.pkcs5_keyivgen(ENV['ENC_PASS'] || "foobar",
+ ENV['ENC_SALT'] || "sodiumcl")
+ cipher = aes.update(@prob[:plaintext])
+ cipher << aes.final
+ session[:soln] = cipher.unpack('H*').join
+ msg = ''
end
key = ENV['HMAC_KEY'] || "derp"
session[:key] = OpenSSL::HMAC.hexdigest('sha256', msg, key)
@@ -95,7 +113,54 @@ def solution
correct = false
perf = -1
- if contest.puzzle_ident == 2
+ if contest.puzzle_ident == 3
+ session.delete :key
+
+ time_elapsed = Time.now.to_i - session[:time]
+ session.delete :time
+
+ level = params[:level]
+ max_time_allowed = level == 0 ? 120 : 300
+ if time_elapsed > max_time_allowed
+ redirect_to contest,
+ alert: "Sorry, you took too long with your answer (#{time_elapsed} seconds)"
+ return
+ end
+
+ begin
+ aes = OpenSSL::Cipher::Cipher.new("AES-128-CBC")
+ aes.decrypt
+ aes.pkcs5_keyivgen(ENV['ENC_PASS'] || "foobar",
+ ENV['ENC_SALT'] || "sodiumcl")
+ our_plain = aes.update(session[:soln].split.pack('H*'))
+ our_plain << aes.final
+ rescue OpenSSL::Cipher::CipherError => e
+ redirect_to contest,
+ alert: "Something funny happened there, try again"
+ return
+ ensure
+ session.delete :soln
+ end
+
+ correctp = ContestsHelper::Dojo3.verify_puzzle(level.to_i,
+ our_plain,
+ params[:solution])
+ if correctp
+ if ENV["RAILS_ENV"] == "production"
+ Pony.mail(
+ :to => 'rafal.dittwald@gmail.com', :cc => 'james.nvc@gmail.com',
+ :from => 'dojobot@hackeracademy.org',
+ :subject => "#{current_user.name} has solved problem #{level} at #{Time.now}")
+ end
+ redirect_to contest, notice: 'Congratulations! Your solution was correct!'
+ current_user.solved ||= []
+ current_user.solved << ["dojo#{contest.puzzle_ident}_level#{level}", Time.now]
+ current_user.save
+ else
+ redirect_to contest, alert: 'Sorry, your solution was incorrect'
+ end
+ return
+ elsif contest.puzzle_ident == 2
msg = nil
level = params[:level]
@@ -129,8 +194,6 @@ def solution
elsif level == '2'
correct,perf = ContestsHelper::Dojo2.verify_level2(params[:searches], params[:locations], params[:solution])
end
-
-
elsif contest.puzzle_ident == 1
puzzle = params[:puzzle].gsub('N', "\n")
words = params[:words].split('+')
@@ -177,7 +240,7 @@ def solution
)
end
end
-
+
end
@@ -206,7 +269,7 @@ def solution
current_user.solved << ["dojo#{contest.puzzle_ident}_level#{level}", Time.now]
current_user.save
else
-
+
if perf != -1
if ENV["RAILS_ENV"] == "production"
@@ -30,6 +30,90 @@ def self.verify_puzzle(dojo, level, args)
return self.const_get(:"Dojo#{dojo}").verify_puzzle(level, *args)
end
+ WORDS = Marshal.load(open('lib/words2.dump'))
+
+ module Dojo3
+
+ TEXT = open('lib/corpus.txt').readlines.map(&:chomp).map(&:downcase)
+
+ def self.shift_encode text, shift
+ return text.split(//).map do |char|
+ (((char.ord - 'a'.ord + shift) % 26) + 'a'.ord).chr
+ end.join
+ end
+
+ # Level 0: Shift cipher with a given shift - spaces removed
+ def self.generate_level0
+ phrase = TEXT[rand(TEXT.length), 3].join.gsub(/[^a-z]/, '')
+ shift = rand 26
+ shifted = shift_encode phrase, shift
+ return {shift: shift, ciphertext: shifted, plaintext: phrase}
+ end
+
+ def self.verify_level0 our_plaintext, their_plaintext
+ return our_plaintext == their_plaintext
+ end
+
+ # Level 1: Shift cipher
+ def self.generate_level1
+ # Only difference is that we don't give the user the shift
+ generate_level0
+ end
+
+ def self.verify_level1 our_plaintext, their_plaintext
+ return our_plaintext == their_plaintext
+ end
+
+ # Level 2: Arbitrary substitution cipher
+ def self.generate_level2
+ alphabet = ('a'..'z').to_a + [' ', '.', ',', ':', ';']
+ encoding = alphabet.zip(alphabet.shuffle).inject({}) do |hsh, (k,v)|
+ hsh[k] = v
+ hsh
+ end
+ phrase = TEXT[rand(TEXT.length), 6].join(' ')
+ ciphered = phrase.split(//).map {|char| encoding[char] }.join
+ return {plaintext: phrase, ciphertext: ciphered}
+ end
+
+ def self.verify_level2 our_plaintext, their_plaintext
+ return our_plaintext == their_plaintext
+ end
+
+ # XOR cipher with fragment of plaintext given
+ def self.generate_level3
+ phrase = TEXT[rand(TEXT.length), 3].join(' ').split
+ key = WORDS.sample
+ hint = phrase.sample
+ while hint.length < 3
+ hint = phrase.sample
+ end
+ attempts = 0
+ while hint.length < key.length
+ hint = phrase[rand(phrase.length), 2 + (attempts % 10)].join ' '
+ attempts += 1
+ end
+ plain = phrase.join(' ').split(//)
+ ciphered = plain.zip((key * ((plain.length / key.length) + 1)
+ ).split(//)).map do |(char, k)|
+ char.ord ^ k.ord
+ end.join ' '
+ return {plaintext: phrase.join(' '), ciphertext: ciphered, hint: hint}
+ end
+
+ def self.verify_level3 our_plaintext, their_plaintext
+ return our_plaintext == their_plaintext
+ end
+
+ def self.generate_puzzle(level, *args)
+ return self.send("generate_level#{level}", *args)
+ end
+
+ def self.verify_puzzle(level, *args)
+ return self.send("verify_level#{level}", *args)
+ end
+ end
+
module Dojo2
POSTS = Marshal.load(open('lib/ecdojoposts.dump'))
@@ -46,7 +130,7 @@ def self.generate_level0
def self.verify_level0(posts, query, soln)
s = soln.split("\n").map{|x| x.strip}.join("+")
-
+
times_ids = []
posts.split('+').each do |post_id|
@@ -124,7 +208,7 @@ def self.generate_level1
hash_searches << [hash, search]
end
-
+
return {searches: hash_searches, locations: locations}
end
@@ -1,4 +1,4 @@
-%p You have 120 seconds to submit a solution:
+%p= "You have #{@max_time_allowed} seconds to submit a solution:"
= form_tag("/contests/solution", method: 'POST') do
= hidden_field_tag :level, @level
= hidden_field_tag :contest, @contest.id
@@ -0,0 +1,31 @@
+= render partial: 'problem_header'
+%p Simple Caeser Cipher
+
+%p
+ You've been given a block of text that's been stripped of spaces and
+ punctuation, made all lower-case, and encoded with a Caeser (shift) cipher,
+ with the shift given to you. Give the plaintext.
+
+%h2 Sample Problem
+%h4 Shift
+.data
+ %code
+ 13
+%h4 Ciphertext
+.data
+ %code
+ wnpxqnjfybirzlovtfcuvakbsdhnegm
+%h4 Solution
+.data
+ %code
+ jackdawslovemybigsphinxofquartz
+
+%h2 Actual Problem
+%h4 Shift
+.data
+ %code= @prob[:shift]
+%h4 Ciphertext
+.data
+ %code= @prob[:ciphertext]
+= render partial: 'submit'
+
@@ -0,0 +1,25 @@
+= render partial: 'problem_header'
+%p Simple Caeser Cipher Part 2
+
+%p
+ You've been given a block of text that's been stripped of spaces and
+ punctuation, made all lower-case, and encoded with a Caeser (shift) cipher.
+ Give the plaintext.
+
+%h2 Sample Problem
+%h4 Ciphertext
+.data
+ %code
+ wnpxqnjfybirzlovtfcuvakbsdhnegm
+%h4 Solution
+.data
+ %code
+ jackdawslovemybigsphinxofquartz
+
+%h2 Actual Problem
+%h4 Ciphertext
+.data
+ %code= @prob[:ciphertext]
+= render partial: 'submit'
+
+
@@ -0,0 +1,27 @@
+= render partial: 'problem_header'
+%p Substitution Cipher
+
+%p
+ You've been given a block of text that's been encoded with a basic
+ substitution cipher - that is, there is a one-to-one mapping between each
+ character which you must determine.
+%p
+ Note that in this case, you may not be able to complete automate the solving
+ \- don't discount looking at intermediate output for clues!
+
+%h2 Sample Problem
+%h4 Ciphertext
+.data
+ %code
+ wnpxqnjfybirzlovtfcuvakbsdhnegm
+%h4 Solution
+.data
+ %code
+ jackdawslovemybigsphinxofquartz
+
+%h2 Actual Problem
+%h4 Ciphertext
+.data
+ %code= @prob[:ciphertext]
+= render partial: 'submit'
+
@@ -0,0 +1,44 @@
+= render partial: 'problem_header'
+%p XOR Cipher
+
+%p
+ You are given a series of decimal numbers that are the result of XOR'ing the
+ ASCII values of the plaintext with a key, e.g. if our plain text is "my plain
+ text" and the key is "key":
+ .data
+ %code
+ &nbsp;&nbsp;&nbsp;m&nbsp;&nbsp;y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;l&nbsp;&nbsp;a&nbsp;i&nbsp;&nbsp;n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t&nbsp;e&nbsp;x&nbsp;&nbsp;t
+ %br
+ ^&nbsp;&nbsp;k&nbsp;&nbsp;e&nbsp;&nbsp;y&nbsp;&nbsp;k&nbsp;e&nbsp;&nbsp;y&nbsp;k&nbsp;&nbsp;e&nbsp;&nbsp;y&nbsp;&nbsp;k&nbsp;e&nbsp;y&nbsp;&nbsp;k
+ %br
+ \--------------------------------------------
+ %br
+ &nbsp;&nbsp;&nbsp;6 28 89 27 9 24 2 11 89 31 0 1 31
+
+ To help you solve this, we've given you a word or two that appear (in the
+ order shown) in the plaintext.
+%p
+ Note that the key will be a dictionary word.
+
+%h2 Sample Problem
+%h4 Hint
+.data
+ %code
+ plain
+%h4 Ciphertext
+.data
+ %code
+ 6 28 89 27 9 24 2 11 89 31 0 1 31
+%h4 Solution
+.data
+ %code
+ my plain text
+
+%h2 Actual Problem
+%h4 Hint
+.data
+ %code= @prob[:hint]
+%h4 Ciphertext
+.data
+ %code= @prob[:ciphertext]
+= render partial: 'submit'
@@ -12,7 +12,7 @@
%p!= @contest.problem
%h2 Solve It!
%ol
- - (0..2).each do |lvl|
+ - (0..@num_probs).each do |lvl|
%li
= "Problem #{lvl}:"
- if current_user.puzzle_available? @contest.puzzle_ident, lvl
Oops, something went wrong. Retry.

0 comments on commit 5216127

Please sign in to comment.