01 July 2009

Vigenere cypher in ruby

Some time ago I implemented the Vigenere as a first look at Python. For some time I’ve wanted to do the same thing in Ruby and I finally found time.

The tests:

 1 require 'test/unit'
 2 require 'vigenere_cipher'
 3 
 4 class VigenereCipherTest < Test::Unit::TestCase
 5 
 6   def test_should_encrypt_a_string
 7     plain_text = 'ATTACKATDAWN';
 8     expected_result = 'LXFOPVEFRNHR';
 9 
10     machine = VigenereCipher.new;
11     encrypted_string = machine.encrypt('LEMONLEMONLE',plain_text);
12 
13     assert_equal expected_result, encrypted_string
14   end
15 
16   def test_should_decrypt_a_string
17     expected_result = 'ATTACKATDAWN';
18     encrypted_message = 'LXFOPVEFRNHR';
19 
20     machine = VigenereCipher.new;
21     encrypted_message = machine.decrypt('LEMONLEMONLE',encrypted_message);
22 
23     assert_equal expected_result, encrypted_message
24   end
25 
26   def test_should_loop_around_the_key_when_it_is_short_than_the_text
27     plain_text = 'ATTACKATDAWN';
28     expected_result = 'JXLSLOSLMEOF';
29 
30     machine = VigenereCipher.new;
31     encrypted_string = machine.encrypt('JESS',plain_text);
32 
33     assert_equal expected_result, encrypted_string
34   end
35 
36 end

And the implementation:

 1 class VigenereCipher
 2 
 3   def encrypt(key, plain_text)
 4     transform(key, plain_text, 'encrypt')
 5   end
 6 
 7   def decrypt(key, encrypted_message)
 8     transform(key, encrypted_message, 'decrypt')
 9   end
10 
11 private
12   def transform(key, text, method)
13     n = -1
14     text.split(//).map{|letter| transform_letter(letter, calc_key_letter(key, n+=1), method=='encrypt' ? :+ : :- )}.join
15   end
16 
17   def transform_letter(letter, key_letter, plus_or_minus)
18     charset_offset = "A"[0]
19     letter_position = letter[0] - charset_offset
20     key_letter_position = key_letter[0]-charset_offset
21 
22     letter_shifts_by = letter_position.send(plus_or_minus, key_letter_position)
23     (letter_shifts_by.modulo(26)+charset_offset).chr
24   end
25 
26   def calc_key_letter(key, n)
27     n = n.modulo(key.length)
28     key.split(//)[n]
29   end
30 
31 end

If you’d like to download the code you can get it from my github account