Skip to content

Commit

Permalink
master: refactoring, doc, roadmap
Browse files Browse the repository at this point in the history
  • Loading branch information
jodell committed Apr 22, 2010
1 parent ca01da7 commit b1276f3
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 84 deletions.
1 change: 1 addition & 0 deletions ROADMAP
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
0.4
-==-
* Rudimentary frequency analysis
* Caesar brute force guessing

1.1

Expand Down
5 changes: 4 additions & 1 deletion lib/toycipher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
module ToyCipher
CIPHERS = %w[caesar otp playfair vigenere shift rot13]
VERSION = 0.3
VERSION_STR = "ToyCipher v#{VERSION} by Jeffrey O'Dell <[email protected]>, http://github.com/jodell/toycipher"
AUTHOR = "Jeffrey O'Dell"
EMAIL = "[email protected]"
URL = 'http://github.com/jodell/toycipher'
VERSION_STR = "ToyCipher v#{VERSION} by #{AUTHOR} <#{EMAIL}>, #{URL}"

require 'toycipherutil'
require 'toycipherbase'
Expand Down
10 changes: 4 additions & 6 deletions lib/toycipher/caesar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
require 'toycipher'

module ToyCipher

# Monoalphabetic cipher that shifts the alphabet by N characters
#
class Caesar < ToyCipherBase
include ToyCipherUtil
#include ToyCipherUtil
attr_writer :offset
attr_accessor :guess

Expand All @@ -20,9 +23,6 @@ def brute
@guess
end

# Caesar encryption
# Monoalphabetic cipher that shifts the alphabet by N characters
#
def encrypt(plaintext = @plaintext, offset = @offset)
offset = normalize_key(offset)
#puts "Trying to decrypt #{ciphertext} with offset #{offset}:#{offset.class}"
Expand All @@ -31,8 +31,6 @@ def encrypt(plaintext = @plaintext, offset = @offset)
@ciphertext
end

# Caesar decryption
#
def decrypt(ciphertext = @ciphertext, offset = @offset)
offset = normalize_key(offset)
#puts "Trying to decrypt #{ciphertext} with offset #{offset}:#{offset.class}"
Expand Down
16 changes: 8 additions & 8 deletions lib/toycipher/otp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
require 'toycipher'

module ToyCipher

# One-time pad
# NOTE: A one-time pad is calculated identically to the Vigenere
# cipher with the exception of the constraint that the key size
# should match the plain/cipher text. Need to confirm official
# sources and potentially refactor.
#
class Otp < ToyCipherBase
include ToyCipherUtil

# One-time pad encryptio
# TODO - Change warning?
#
def encrypt(plaintext = @plaintext, key = @key)
if plaintext.size != key.size
raise ToyCipherException, "#{self.class}: Key length should be equal to the length of message!"
Expand All @@ -19,9 +18,10 @@ def encrypt(plaintext = @plaintext, key = @key)
@ciphertext = xor results.first, results.last
end

# One-time pad decryption
#
def decrypt(ciphertext, key)
# if ciphertext.size != key.size
# raise ToyCipherException, "#{self.class}: Key length should be equal to the length of message!"
# end
results = pad_key normalize(ciphertext), normalize(key)
@plaintext = inv_xor results.first, results.last
end
Expand Down
13 changes: 6 additions & 7 deletions lib/toycipher/playfair.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ module ToyCipher
class PlayfairException < Exception; end

class Playfair < ToyCipherBase
include ToyCipherUtil

attr_reader :keyblock, :ommit_letter, :fill_letter, :transforms

VALID_OMMIT_LETTERS = ['Q', 'I', 'J']
OMMIT_DEFAULT = VALID_OMMIT_LETTERS.first
VALID_FILL_LETTERS = ['X', 'Q']
FILL_DEFAULT = 'X'

def initialize()
def initialize
super
set_ommit_letter; set_fill_letter; set_tranformations
set_ommit_letter
set_fill_letter
set_tranformations
end

def possible?(str = @ciphertext)
Expand Down Expand Up @@ -89,7 +90,7 @@ def set_tranformations
# x1 - x0 != 0 && y1 - y0 = 0
# p2 = (x0 + 1, y0), p3 = (x1 + 1, y0)
#
def transform_digraph(str, mode)
def transform_digraph(str, mode)
l0 = xy_pos str[0].chr; l1 = xy_pos str[1].chr
x0, y0 = l0.first, l0.last
x1, y1 = l1.first, l1.last
Expand Down Expand Up @@ -122,7 +123,6 @@ def xy_pos(letter)
def letter_at(pos)
@keyblock[ (pos.first) % 5 + ( (pos.last % 5) * 5) ]
end


# Normalize a string and return it in pairs, splitting pairs with the
# @fill_letter and padding if it is of odd length. Should work for both
Expand Down Expand Up @@ -169,7 +169,6 @@ def pp

# TODO
def to_s() super end

end # Playfair

end # ToyCipher
Expand Down
43 changes: 0 additions & 43 deletions lib/toycipher/toycipherbase.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@


module ToyCipher

class ToyCipherException < Exception; end
Expand All @@ -8,7 +7,6 @@ class ToyCipherException < Exception; end
#
class ToyCipherBase
include ToyCipherUtil

attr_accessor :plaintext, :ciphertext, :key, :alph

def initialize
Expand All @@ -24,48 +22,11 @@ def decrypt
nil # Subclass responsibility'
end

# Implementation of ROT13
#
def rot13(str)
normalize(str).split(//).inject('') { |acc, b| acc += mod_shift(b, @alph.size/2) }
end

# Pad key if needed
#
def pad_key(plaintext, key)
key += key while key.size < plaintext.size
key = key.slice(0, plaintext.size) if key.size > plaintext.size
[plaintext, key]
end

# One time pad encryption
#
def otp(plaintext, key)
results = pad_key normalize(plaintext), normalize(key)
xor results.first, results.last
end

def to_s
"<%s: id=%d, plaintext='%s', key='%s', ciphertext='%s', alph='%s'>" %
[self.class, self.object_id, @plaintext, @key, @ciphertext, @alph]
end

################
# Alphabet
#####
def generate_alphabet
('A'..'Z').inject([]) { |acc, l| acc << l }
end

##
# Rotate the alphabet, default A = 0. Other common cases are A = 1.
#
def rotate_alphabet(offset)
alph = generate_alphabet
(offset % alph.size).times { alph.unshift(alph.pop) }
alph
end

def rotate_alphabet!(offset)
@alph = rotate_alphabet(offset)
end
Expand All @@ -76,10 +37,6 @@ def rotate_alphabet!(offset)
def reset_alphabet!
@alph = generate_alphabet
end
################

end # ToyCipherBase

end # ToyCipher


63 changes: 55 additions & 8 deletions lib/toycipher/toycipherutil.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

module ToyCipher
# Utility Class, provides pretty print, distribution graph, etc.

# Utility module, provides pretty print, distribution graph, etc.
#
module ToyCipherUtil
EN_US_FREQ_RANK = %w(E R S T L N) unless defined?(EN_US_FREQ_RANK)

Expand All @@ -18,7 +20,6 @@ def distribution(str, alph)
str.each_byte { |b| dist[b.chr] = dist[b.chr] + 1 }
dist
end


# Pretty Print of a character frequency hash
#
Expand Down Expand Up @@ -50,9 +51,8 @@ def map_dist_to_rank(dist, alph = EN_US_FREQ_RANK)
def normalize(str)
str.gsub(/\W/, '').upcase
end

# This does a matrix transposition on an m x n array of strings,
# returning n x m

# DEPREACTED unless there are proven performance gains over transpose
def self.transpose2(arr)
cols = arr.first.size
raise Exception, "#{self.class}: Inconsistent string lengths in matrix! " if arr.any? { |l| l.size != cols }
Expand All @@ -69,6 +69,8 @@ def self.transpose2(arr)
ans
end

# This does a matrix transposition on an m x n array of strings,
# returning n x m
def self.transpose(arr)
cols = arr.first.size
raise Exception, "#{self.class}: Inconsistent string lengths in matrix! " if arr.any? { |l| l.size != cols }
Expand All @@ -78,11 +80,9 @@ def self.transpose(arr)
end


###########
#######
# Modular arithmetic
#####

##
# addition
#
def mod_add(chr1, chr2)
Expand All @@ -104,6 +104,35 @@ def mod_shift(chr, offset)
@alph[(@alph.index(chr) + offset) % @alph.length]
end

########

# Pad key if needed
#
def pad_key(plaintext, key)
key += key while key.size < plaintext.size
key = key.slice(0, plaintext.size) if key.size > plaintext.size
[plaintext, key]
end

########
# CHEAP CIPHERS
#####

# One time pad encryption
#
def otp(plaintext, key)
results = pad_key normalize(plaintext), normalize(key)
xor results.first, results.last
end

# Implementation of ROT13
#
def rot13(str)
normalize(str).split(//).inject('') { |acc, b| acc += mod_shift(b, @alph.size / 2) }
end

########

##
# Perform modular subtraction per character as: lhs - rhs
# TODO - Performance comparison of implementations
Expand Down Expand Up @@ -137,6 +166,24 @@ def xor(str1, str2)
acc += mod_add(ch.first, ch.last)
end
end

########
# Alphabet
#####
def generate_alphabet
('A'..'Z').inject([]) { |acc, l| acc << l }
end

##
# Rotate the alphabet, default A = 0.
# Other common cases are A = 1.
#
def rotate_alphabet(offset)
alph = generate_alphabet
(offset % alph.size).times { alph.unshift(alph.pop) }
alph
end
########
end # ToyCipherUtil

end # ToyCipher
12 changes: 1 addition & 11 deletions lib/toycipher/vigenere.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,18 @@
module ToyCipher

# Vigenere cipher
# TODO: MORE DOC
#
class Vigenere < ToyCipherBase
include ToyCipherUtil

def initialize
super
end

# Vigenere encryption
#
def encrypt(plaintext = @plaintext, key = @key)
results = pad_key normalize(plaintext), normalize(key)
@ciphertext = xor results.first, results.last
end

# Vigenere decryption
#
def decrypt(ciphertext, key)
results = pad_key normalize(ciphertext), normalize(key)
@plaintext = inv_xor results.first, results.last
end

end # Vigenere

end # ToyCipher
Expand Down

0 comments on commit b1276f3

Please sign in to comment.