62 lines
1.6 KiB
Crystal
62 lines
1.6 KiB
Crystal
def make_square
|
|
# it's a square at heart...
|
|
(('A'..'Z').to_a + ('0'..'9').to_a).shuffle!
|
|
end
|
|
|
|
def make_key (size)
|
|
words = File.open("unixdict.txt") do |f|
|
|
f.each_line.select {|w| w.size == size && w.chars.to_set.size == size }.to_a
|
|
end
|
|
raise "no suitable key found" if words.empty?
|
|
words.sample
|
|
end
|
|
|
|
def adfgvx_encrypt (message, square, key)
|
|
h = "ADFGVX".chars
|
|
message.chars.flat_map {|ch|
|
|
row, col = square.index!(ch).divmod(6)
|
|
[ h[row], h[col] ]
|
|
}
|
|
.in_groups_of(key.size)
|
|
.transpose
|
|
.zip(key.chars)
|
|
.sort_by {|_, key| key }
|
|
.map {|col, _| col.join }
|
|
.join(" ")
|
|
end
|
|
|
|
def adfgvx_decrypt (message, square, key)
|
|
h = "ADFGVX".chars
|
|
sorted_key = key.chars.sort!
|
|
cols = message.split.map &.chars
|
|
max_size = cols.max_of &.size
|
|
cols.map {|col| col + [nil]*(max_size-col.size) }
|
|
.zip(sorted_key)
|
|
.sort_by {|_, ch| key.index! ch }
|
|
.map {|col, _| col }
|
|
.transpose
|
|
.flatten.compact
|
|
.in_slices_of(2)
|
|
.map {|(row, col)| square[ h.index!(row)*6 + h.index!(col) ] }
|
|
.join
|
|
end
|
|
|
|
def show (title, message, square, key)
|
|
encrypted = adfgvx_encrypt message, square, key
|
|
decrypted = adfgvx_decrypt encrypted, square, key
|
|
puts title
|
|
puts " - Alphabet: #{square.join}"
|
|
puts " - Key: #{key}"
|
|
puts " - Message: #{message}"
|
|
puts " - Encrypted: #{encrypted}"
|
|
puts " - Decrypted: #{decrypted}"
|
|
puts
|
|
end
|
|
|
|
message = "ATTACKAT1200AM"
|
|
|
|
wiki_square = "NA1C3H8TB2OME5WRPD4F6G7I9J0KLQSUVXYZ".chars
|
|
wiki_key = "PRIVACY"
|
|
|
|
show "Wikipedia example:", message, wiki_square, wiki_key
|
|
show "Random square and key:", message, make_square, make_key(9)
|