Skip to content

Instantly share code, notes, and snippets.

@thomasbrus
Last active August 29, 2015 13:57
Show Gist options
  • Save thomasbrus/9677033 to your computer and use it in GitHub Desktop.
Save thomasbrus/9677033 to your computer and use it in GitHub Desktop.
Semi-automatic Vigenère cipher decrypter.
xsjztliyjvphmamicnwlriemsotjpsgcdtensyyllyydjwlxicnidtjonjqjvpsxnfidfvnntsjvdgedjhzsxsjppyxpwwzkevjchtvonxtxednqaqiqtvxtjatpjfpamemjxthwfgwenxfymzsxsjztliyjvphmamicmedgipsvpnrgjrejhxfrjymxjwemixjxsthhfwzwmrnrlqpjiidhvtgiogcrnsgfrmfxtxxlgiwfwznrsnwqnjejiykmqycemvpjfztowfgtkvliiwxmrlmzaeygeenweffpqedtlzbigjvemidhlprihfwwfxpwqtxeeyvtgyejhetfwfmdjhpamrjrpwitsxsjrtsiejiyylnjrezvjfronwytahnhpqcvsshsedylpamrjrpwinntsjvemmdhmamicnwhjpwprzbrmjglzwpbltqitymdjeddxzzrojvdyeyieyimxuppriyymetjejrlutpfvdysmjktsrpwwetfpzrmwilpemqiemmdjecsionxemiojwnwmaymzspphltkjcjmyiinmmqkvlgppkvpsgskscylpzrmwilpemqinntsjvntrdjufjreqcxfrjuizuppmegjxcnioystrtwjqpsxzgjfxglymzsscjrnwcaymzswnmixjwemeefvpjwdjrenewqcgnkpsicjgtulpwwzspjyssfzpylprfctopsxsjwewiylxsgisnroylpamrjrpwinntsjvwnopfpwuswdewullgiengnntsjvdnwtywlgmwnxjysdycxniqwibziyhclsewdwtxjcjufjrndeyfpjxmdnwemiawenymnjsqiinwcaymylexjwdfkpgcntyyymylxsjjcjufjrndsqhmamicyiiyppyxpwwlshpvylymylmeysemiwjxejvqwibziyhczkrzwqlqxpcxqtvtswefrnjmqusnhycwiorsdymyfgtulpwxpcxhmsdjtwfmyyiiymdnrpskwnwstrphsfqhdzwajgeyllytntvcjwatrojhetimjglzwpjmdylprsdyjcjufjreqcfxioqieyicnrpskwnwszwtskemignkpsicjgtulpwinfrmjiyhmamicjhlxeydsqxigjvlqppyxpwwtsxsjewullgiefxonjqjvpsxatmyywtsxsjqpxwlliemydiiqjeenrrxmxuppkvpvypsgjfrlqcdnweminwmenglqapfoyjwdnremignkpsicjgtulpwmdylpwiwfxtaiwdwstvefrowiajeejhyfxfwizkmexopdmqfgcdtefrlqcdyhtxgzaicxxsjopdwwjrrylemiyylphmamicyiiyglsfpyvpfxpiedfwpwmpxsqimqkicjrehepxechmamicxasngsnronztiylqpjfvpyvtamlqpjgvzpiyylppednwvneyijcnioreyyidywsjpaiiejvxnrpfgtulpwxpcxdpijqiylxsyltxxpcxtxjctqhnotuione
module Enumerable
def frequencies
self.to_a.inject(Hash.new(0)) do |frequencies, element|
frequencies.tap { |fqs| fqs[element] += 1 }
end
end
end
class CaesarCipher
attr_reader :shift
def initialize(shift, alphabet = Array('a'..'z').join)
@shift = shift % alphabet.length
@alphabet = alphabet
end
def shifted_alphabet
@alphabet[@shift..-1] + @alphabet[0...@shift]
end
def encrypt(string)
string.tr(@alphabet, shifted_alphabet)
end
def decrypt(string)
string.tr(shifted_alphabet, @alphabet)
end
end
class VigenereCipher
attr_reader :key
attr_writer :caeser_cipher_factory
def initialize(key, alphabet = Array('a'..'z').join)
@key, @alphabet = key, alphabet
end
def encrypt(string)
process(string) { |cipher, char| cipher.encrypt(char) }.flatten.join
end
def decrypt(string)
process(string) { |cipher, char| cipher.decrypt(char) }.flatten.join
end
def caesar_cipher_factory
@caesar_cipher_factory ||= CaesarCipher.public_method(:new)
end
private
def process(string)
string.chars.each_slice(key.length).map do |characters|
characters.zip(caesar_ciphers).map do |character, cipher|
yield(cipher, character)
end
end
end
def caesar_ciphers
key.each_char.map do |key_letter|
caesar_cipher_factory.call(key_letter.ord - 'a'.ord, @alphabet)
end
end
end
encrypted_text = IO.read('./cipher.txt').strip
# Guessing...
sequence_length = n = 3
# Analyse letter frequencies
letter_frequencies = n.times.map do |offset|
(offset...encrypted_text.length).step(n).map { |i| encrypted_text[i] }.frequencies
end
# Find the most frequent letters
most_frequent_letters = letter_frequencies.map do |lf|
lf.max_by { |_, frequency| frequency }[0]
end
# Calculate key
key = most_frequent_letters.inject('') do |key, most_frequent_letter|
key << CaesarCipher.new(most_frequent_letter.ord - 'e'.ord).encrypt('a')
end
# Decrypt text
puts VigenereCipher.new(key).decrypt(encrypted_text)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment