Last active
October 16, 2015 15:35
-
-
Save wjessop/00d7d97b321499fa7640 to your computer and use it in GitHub Desktop.
Decrypt some things!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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