Hello, ancient cryptography being one of my hobbies thanks to David Kahn's book The Codebreakers, I'm trying implement in Ruby classes to handle old ciphers such as the Nihilist cipher and ADFGVX. For these, one useful item is the Straddling checkboard. I have the following implementation in Ruby and would welcome any improvement.
class Key
is the base class (a virtual class if you want). Key#condensed
is a method that remove duplicated letters from a given word.
class SKey < Key
attr_reader :full_key
attr_reader :alpha, :ralpha
def initialize(key)
super(key)
@alpha = Hash.new
@ralpha = Hash.new
@full_key = checkboard()
gen_rings()
end
# === checkboard
#
# Shuffle the alphabet a bit to avoid sequential allocation of the
# code numbers
#
# Regular rectangle
# -----------------
# Key is ARABESQUE condensed into ARBESQU (len = 7) (height = 4)
# Let word be ARBESQUCDFGHIJKLMNOPTVWXYZ/-
#
# First passes will generate
#
# A RBESQUCDFGHIJKLMNOPTVWXYZ/- c=0 0 x 6
# AC RBESQUDFGHIJKLMNOPTVWXYZ/- c=6 1 x 6
# ACK RBESQUDFGHIJLMNOPTVWXYZ/- c=12 2 x 6
# ACKV RBESQUDFGHIJLMNOPTWXYZ/- c=18 3 x 6
# ACKVR BESQUDFGHIJLMNOPTWXYZ/- c=0 0 x 5
# ACKVRD BESQUFGHIJLMNOPTWXYZ/- c=5 1 x 5
# ...
# ACKVRDLWBFMXEGNYSHOZQIP/UJT-
#
# Irregular rectangle
# -------------------
# Key is SUBWAY condensed info SUBWAY (len = 6) (height = 5)
#
# S UBWAYCDEFGHIJKLMNOPQRTVXZ/- c=0 0 x 5
# SC UBWAYDEFGHIJKLMNOPQRTVXZ/- c=5 1 x 5
# SCI UBWAYDEFGHJKLMNOPQRTVXZ/- c=10 2 x 5
# SCIO UBWAYDEFGHJKLMNPQRTVXZ/- c=15 3 x 5
# SCIOX UBWAYDEFGHJKLMNPQRTVZ/- c=20 4 x 5
# SCIOXU BWAYDEFGHJKLMNPQRTVZ/- c=0 0 x 4
# ...
# SCIOXUDJPZBEKQ/WFLR-AG YHMNTV c=1 1 x 1
# SCIOXUDJPZBEKQ/WFLR-AGM YHNTV c=2 2 x 1
# SCIOXUDJPZBEKQ/WFLR-AGMT YHNV c=3 3 x 1
# SCIOXUDJPZBEKQ/WFLR-AGMTYHNV
#
def checkboard
word = (@key + BASE).condensed.dup
len = @key.condensed.length
height = BASE.length / len
# Odd rectangle
#
if (BASE.length % len) != 0
height = height + 1
end
print "\ncheckboard size is #{len} x #{height}\n"
res = ""
(len - 1).downto(0) do |i|
0.upto(height - 1) do |j|
if word.length <= (height - 1) then
return res + word
else
c = word.slice!(i * j)
if not c.nil? then
res = res + c.chr
end
end
end
end
return res
end
# == gen_rings
#
# Assign a code number for each letter. Each code number is
# sequentially allocated from two pools, one with 0..7 and
# the other with 80..99.
#
# Allocation is made on the following criterias
# - if letter is one of ESANTIRU assign a single code number
# - else assign of of the two letters ones
#
# Generate both the encoding and decoding rings.
#
# XXX FIXME Use of 80-99 is hardcoded
#
def gen_rings
ind_u = 0
ind_d = 80
word = @full_key.dup
word.scan(/./) do |c|
if c =~ /[ESANTIRU]/
@alpha[c] = ind_u
@ralpha[ind_u] = c
ind_u = ind_u + 1
else
@alpha[c] = ind_d
@ralpha[ind_d] = c
ind_d = ind_d + 1
end
end
end # -- gen_rings
Ruby/Python/Perl or pseudo-code is fine for me. Thanks for any idea.