Skip to content

Instantly share code, notes, and snippets.

@wjessop
Last active October 16, 2015 15:35
Show Gist options
  • Select an option

  • Save wjessop/00d7d97b321499fa7640 to your computer and use it in GitHub Desktop.

Select an option

Save wjessop/00d7d97b321499fa7640 to your computer and use it in GitHub Desktop.
Decrypt some things!
# 1. Determine possible keywords by examining all dict words of the correct length and checking to see if when un-shifted they match the key
# (note: it turns out there is only *one* word in /usr/share/dict/words) that matches the key when it is shifted
# 2. Decrypt the ciper text with all candidate keys
# 3. Count how many dict words appear in each candidate decrypt and sort by the count, display them in order
# Outputs:
# 278 dict words: PRETENDTHATYOUVEOPENEDTHISBOOKALTHOUGHYOUPROBABLYHAVEOPENEDTHISBOOKJUSTTOFINDAHUGEONIONRIGHTINTHEMIDDLECREASEOFTHEBOOKTHEMANUFACTUREROFTHEBOOKHASINCLUDEDTHEONIONATMYREQUESTSOYOURELIKEWOWTHISBOOKCOMESWITHANONIONEVENIFYOUDONTPARTICULARLYLIKEONIONSIMSUREYOUCANAPPRECIATETHELOGISTICSOFSHIPPINGANYSORTOFPRODUCEDISCREETLYINSIDEOFANALLEGEDPROGRAMMINGMANUAL
CIPHER_TEXT = "EFMZRNQMWOBEBUIXDDMTRDGAXGJUBKNEIVWATHLHJDZUOAOENVIBROCXCSLZUIFUDCSPHSGMDTQTQAUNVSWTVOAKXUPZVNGATAQJQLRVGSIYROSMWSJUBKGATAITHFNVIIZKEOSMWSJUBKUTHWVIYUQXSHPKBNVHCOBSLRRJJSAZFOLHJFMRVKRPDKBNVSOHDYKUZEFPXHPGAOABDBMBRNVYNCCJBNGIPFBOPUYTGZGRVKRHCWWTFIZLJFMEBUPTCOXVEEPBPHMZUEYHVWAZVCFHUGPOCPVGVOVEFOEMDTXXBDHVTRQYPRRXIZGOASVWTCNGAAYETUMJCRBZGOUSVNTFPBCGY"
KEY = "JICAHUHN"
DICT_FILE = "/usr/share/dict/words"
def plaintext_from_cipher_text(key, cipher_text)
i = 0
sentence = []
cipher_text.each_char{|cipher_char|
# Get the key char for the current cipher text number
curr_key_letter = key[i % key.length]
cipher_char_to_key_char_offset = offset_for_letter_from_letter(curr_key_letter, "A")
sentence << shift_str(cipher_char, cipher_char_to_key_char_offset)
i += 1
}
return sentence.join ""
end
def offset_for_letter_from_letter(l1, l2)
l2.ord - l1.ord
end
def shift_str(str, distance)
str.each_char.map{|c| (((c.ord + distance - 65) % 26) + 65).chr}.join("")
end
def key_candidates(dict_words)
candidates = []
dict_words.each{|word|
if word.strip.length == KEY.length && word[4] == word[6]
# Here we have all the words that are the correct length and have repeated letters
# in the correct places. Lets calculate the repeated letter offset and apply it to
# the other letters and see if the dict word matches
word_offset = offset_for_letter_from_letter(word[4], KEY[4])
# Apply the offset to each of the key letters
shifted_key = shift_str(KEY, 0 - word_offset)
candidates << word if shifted_key == word
end
}
candidates
end
dict_words = File.readlines(DICT_FILE).map(&:strip).map(&:upcase)
# Attempt to decrypt the cipertext with all key candidates
decrypt_attempts = key_candidates(dict_words).map {|key|
plaintext_from_cipher_text(key, CIPHER_TEXT)
}
# Count how many dict words each cipher text contains
counted = {}
decrypt_attempts.each {|d|
counted[d] = 0
dict_words.select{|w| w.length > 2}.each {|w|
counted[d] += d.scan(w).size
}
}
# Sort the decrypt candidates by word count and display them in order
counted.sort_by{|str, count| 0-count }.each {|str, count|
puts "#{count} dict words: #{str}"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment