wixy 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/bin/wixy +61 -0
- data/lib/wixy.rb +21 -0
- data/lib/wixy/alphabet.rb +60 -0
- data/lib/wixy/bifid.rb +11 -0
- data/lib/wixy/caesar.rb +26 -0
- data/lib/wixy/config.rb +37 -0
- data/lib/wixy/version.rb +3 -0
- data/lib/wixy/vigenere.rb +53 -0
- data/spec/alphabet_spec.rb +21 -0
- data/spec/bifid_spec.rb +22 -0
- data/spec/caesar_examples_encrypt_discard.yaml +5 -0
- data/spec/caesar_spec.rb +25 -0
- data/spec/config_spec.rb +24 -0
- data/spec/example_helper.rb +14 -0
- data/spec/vigenere_examples_decrypt.yaml +4 -0
- data/spec/vigenere_examples_encrypt_discard.yaml +83 -0
- data/spec/vigenere_examples_encrypt_preserve.yaml +17 -0
- data/spec/vigenere_spec.rb +47 -0
- data/spec/wixy_spec.rb +45 -0
- metadata +126 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Eric Watson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/bin/wixy
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'wixy'
|
5
|
+
|
6
|
+
options = OpenStruct.new
|
7
|
+
config = Wixy::Config.new
|
8
|
+
|
9
|
+
# Defaults
|
10
|
+
options.encrypt = true
|
11
|
+
options.decrypt = false
|
12
|
+
options.mode = :encrypt
|
13
|
+
config.cipher = :caesar
|
14
|
+
config.shift = 3
|
15
|
+
|
16
|
+
begin
|
17
|
+
parser = OptionParser.new do |opts|
|
18
|
+
opts.banner = "Usage: #{__FILE__} [options] TEXT"
|
19
|
+
opts.on("-e", "--encrypt", "Encrypt text (default)") do |e|
|
20
|
+
options.encrypt = e
|
21
|
+
end
|
22
|
+
|
23
|
+
ciphers = config.ciphers
|
24
|
+
cipher_names = ciphers
|
25
|
+
.map {|c| " - [#{c[0]}]#{c[1,c.length-1]}"}
|
26
|
+
|
27
|
+
opts.on("-c", "--cipher CIPHER", ciphers.map(&:to_sym), "Use CIPHER", *cipher_names) do |c|
|
28
|
+
config.cipher = c
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("-d", "--decrypt", "Decrypt text") do |d|
|
32
|
+
options.decrypt = d
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("-k", "--key KEY", "Use KEY to create cipher") do |k|
|
36
|
+
config.key = k
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("-s", "--shift [INTEGER]", Integer, "Alphabet shift") do |s|
|
40
|
+
config.shift = s
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
44
|
+
puts opts
|
45
|
+
exit
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on_tail("-v", "--version", "Show version") do
|
49
|
+
puts Wixy::VERSION
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
end.parse!
|
53
|
+
rescue => e
|
54
|
+
abort e.message
|
55
|
+
end
|
56
|
+
|
57
|
+
text = ARGV.pop
|
58
|
+
abort(parser) unless text
|
59
|
+
options.mode = :decrypt if options.decrypt
|
60
|
+
result = Wixy.public_send(options.mode, text, config)
|
61
|
+
puts result
|
data/lib/wixy.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'wixy/version'
|
2
|
+
require 'wixy/config'
|
3
|
+
require 'wixy/vigenere'
|
4
|
+
require 'wixy/caesar'
|
5
|
+
require 'wixy/bifid'
|
6
|
+
|
7
|
+
module Wixy
|
8
|
+
def self.encrypt(text, config = Config.new)
|
9
|
+
new_cipher(config).encrypt(text)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.decrypt(text, config = Config.new)
|
13
|
+
new_cipher(config).decrypt(text)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def self.new_cipher(config)
|
18
|
+
klass = config.cipher.to_s.capitalize
|
19
|
+
self.const_get(klass).new(config)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Wixy
|
2
|
+
class Alphabet
|
3
|
+
# chars is an array of character strings, like ["A", "B", "C" ...]
|
4
|
+
def initialize(chars, options = {})
|
5
|
+
@chars = chars.to_a.rotate(options[:shift] || 0)
|
6
|
+
@index = Hash[@chars.each_with_index.to_a]
|
7
|
+
@filters = options[:filters] || []
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](index)
|
11
|
+
@chars[index % @chars.length]
|
12
|
+
end
|
13
|
+
|
14
|
+
def index(char)
|
15
|
+
i = @index[filtered char]
|
16
|
+
end
|
17
|
+
|
18
|
+
def include?(char)
|
19
|
+
index(filtered char)
|
20
|
+
end
|
21
|
+
|
22
|
+
def filtered(char)
|
23
|
+
@filters.inject(char) { |c, f| c.public_send(f) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def sanitize(text_or_chars)
|
27
|
+
chars = if text_or_chars.respond_to?(:chars)
|
28
|
+
text_or_chars.chars
|
29
|
+
else
|
30
|
+
text_or_chars
|
31
|
+
end
|
32
|
+
chars.map {|c| filtered(c) }.map { |c| char_if_present(c) }.compact
|
33
|
+
end
|
34
|
+
|
35
|
+
# Given a char and another alphabet, find the char in this alphabet
|
36
|
+
# at the corresponding index
|
37
|
+
def char(char, other_alphabet)
|
38
|
+
self[other_alphabet.index(char)]
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
"<Alphabet:[#{@chars.join}]>"
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def char_if_present(char)
|
47
|
+
char if index(char)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Factories
|
51
|
+
|
52
|
+
# A basic alphabet of capital English letters. Given a lowercase
|
53
|
+
# letter, will find the corresponding uppercase letters. When
|
54
|
+
# using this alphabet to encrypt or decrypt a message, your result
|
55
|
+
# will be all uppercase.
|
56
|
+
def self.AZ(options = {})
|
57
|
+
self.new ('A'..'Z'), {filters: [:upcase]}.merge(options)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/wixy/bifid.rb
ADDED
data/lib/wixy/caesar.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'wixy/config'
|
2
|
+
require 'wixy/alphabet'
|
3
|
+
|
4
|
+
module Wixy
|
5
|
+
class Caesar
|
6
|
+
def initialize(config = Config.new)
|
7
|
+
@text_alphabet = Alphabet.AZ
|
8
|
+
@cipher_alphabet = Alphabet.AZ shift: config.shift
|
9
|
+
end
|
10
|
+
|
11
|
+
def encrypt(text)
|
12
|
+
substitute(text, @text_alphabet, @cipher_alphabet)
|
13
|
+
end
|
14
|
+
|
15
|
+
def decrypt(text)
|
16
|
+
substitute(text, @cipher_alphabet, @text_alphabet)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def substitute(text, source_alphabet, target_alphabet)
|
21
|
+
source_alphabet.sanitize(text).map do |char|
|
22
|
+
target_alphabet.char(char, source_alphabet)
|
23
|
+
end.join
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/wixy/config.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Wixy
|
2
|
+
class Config
|
3
|
+
attr_accessor :cipher
|
4
|
+
attr_accessor :key
|
5
|
+
attr_accessor :shift
|
6
|
+
attr_accessor :preserve
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@cipher = :caesar
|
10
|
+
@shift = 3
|
11
|
+
@key = "FORTIFICATION"
|
12
|
+
@preserve = false
|
13
|
+
yield self if block_given?
|
14
|
+
end
|
15
|
+
|
16
|
+
def cipher=(name)
|
17
|
+
c = name.to_sym
|
18
|
+
unless cipher?(c)
|
19
|
+
raise "Unknown cipher: #{name}. Choose from: #{ciphers.join(', ')}"
|
20
|
+
end
|
21
|
+
@cipher = c
|
22
|
+
end
|
23
|
+
|
24
|
+
def shift=s
|
25
|
+
@shift = s.to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
def cipher?(name)
|
29
|
+
ciphers.include? name.to_sym
|
30
|
+
end
|
31
|
+
|
32
|
+
# An array of available ciphers
|
33
|
+
def ciphers
|
34
|
+
[:caesar, :vigenere]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/wixy/version.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'wixy/config'
|
2
|
+
require 'wixy/alphabet'
|
3
|
+
|
4
|
+
module Wixy
|
5
|
+
class Vigenere
|
6
|
+
def initialize(config = Config.new)
|
7
|
+
@config = config
|
8
|
+
@alphabet = Alphabet.AZ
|
9
|
+
@key = @alphabet.sanitize(config.key)
|
10
|
+
end
|
11
|
+
|
12
|
+
def encrypt(text)
|
13
|
+
shift = -> index, offset { index + offset }
|
14
|
+
solve text, shift
|
15
|
+
end
|
16
|
+
|
17
|
+
def decrypt(text)
|
18
|
+
shift = -> index, offset { index - offset }
|
19
|
+
solve text, shift
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def preserve?
|
24
|
+
@config.preserve
|
25
|
+
end
|
26
|
+
|
27
|
+
def solve(text, shift)
|
28
|
+
result = solve_portion(text.chars.to_a, 0, shift).reverse
|
29
|
+
discard_or_not(result).join
|
30
|
+
end
|
31
|
+
|
32
|
+
def solve_portion(text, i, shift)
|
33
|
+
return text if text.empty?
|
34
|
+
char = text.shift
|
35
|
+
if @alphabet.index(char)
|
36
|
+
solve_portion(text, i + 1, shift) << lookup(char, i, shift)
|
37
|
+
else
|
38
|
+
solve_portion(text, i, shift) << char
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def lookup(char, position, shift)
|
43
|
+
index = @alphabet.index(char.upcase)
|
44
|
+
offset = @alphabet.index(@key[position % @key.length])
|
45
|
+
new_index = shift.call(index, offset)
|
46
|
+
@alphabet[new_index]
|
47
|
+
end
|
48
|
+
|
49
|
+
def discard_or_not(chars)
|
50
|
+
preserve? ? chars : @alphabet.sanitize(chars)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'wixy/alphabet'
|
2
|
+
|
3
|
+
describe "Alphabet" do
|
4
|
+
describe "AZ" do
|
5
|
+
let(:alphabet) { Wixy::Alphabet.AZ }
|
6
|
+
|
7
|
+
it "includes 'Z'" do
|
8
|
+
expect(alphabet).to include('Z')
|
9
|
+
end
|
10
|
+
|
11
|
+
it "includes 'z'" do
|
12
|
+
expect(alphabet).to include('z')
|
13
|
+
end
|
14
|
+
|
15
|
+
it "is case-insensitive for search" do
|
16
|
+
expect(alphabet.index 'A').to eq(0)
|
17
|
+
expect(alphabet.index 'a').to eq(0)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/spec/bifid_spec.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'wixy/bifid'
|
2
|
+
|
3
|
+
|
4
|
+
describe 'Bifid' do
|
5
|
+
let(:bifid) { Wixy::Bifid.new }
|
6
|
+
it "encrypts '' to ''" do
|
7
|
+
result = bifid.encrypt('')
|
8
|
+
expect(result).to eq('')
|
9
|
+
end
|
10
|
+
|
11
|
+
# 'Forsaking monastic tradition, twelve jovial friars gave up their
|
12
|
+
# vocations.'
|
13
|
+
it "encrypts 'F' to 'F'" do
|
14
|
+
result = bifid.encrypt('F')
|
15
|
+
expect(result).to eq('F')
|
16
|
+
end
|
17
|
+
|
18
|
+
it "encrypts 'FO' to 'HD'" do
|
19
|
+
result = bifid.encrypt('FO')
|
20
|
+
expect(result).to eq('HD')
|
21
|
+
end
|
22
|
+
end
|
data/spec/caesar_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'example_helper'
|
2
|
+
require 'wixy/caesar'
|
3
|
+
|
4
|
+
include Wixy
|
5
|
+
|
6
|
+
describe "Caesar" do
|
7
|
+
extend ExampleHelpers
|
8
|
+
|
9
|
+
describe "#encrypt" do
|
10
|
+
load_examples("caesar", "encrypt", "discard").each do |example|
|
11
|
+
describe "'#{example['cleartext']}'" do
|
12
|
+
let(:config) {
|
13
|
+
::Wixy::Config.new do |config|
|
14
|
+
config.shift = example["shift"]
|
15
|
+
end
|
16
|
+
}
|
17
|
+
let(:caesar) { Caesar.new(config) }
|
18
|
+
it "produces '#{example['ciphertext']}'" do
|
19
|
+
result = caesar.encrypt example["cleartext"]
|
20
|
+
expect(result).to eq(example["ciphertext"])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'wixy/config'
|
2
|
+
|
3
|
+
describe "Config" do
|
4
|
+
describe "defaults" do
|
5
|
+
subject(:config) { Wixy::Config.new }
|
6
|
+
its(:shift) { should eq(3) }
|
7
|
+
its(:key) { should eq("FORTIFICATION") }
|
8
|
+
its(:cipher) { should eq(:caesar) }
|
9
|
+
end
|
10
|
+
|
11
|
+
context "with a block" do
|
12
|
+
let(:key) { "KEY" }
|
13
|
+
let(:shift) { 13 }
|
14
|
+
subject(:config) {
|
15
|
+
Wixy::Config.new do |config|
|
16
|
+
config.key = key
|
17
|
+
config.shift = shift
|
18
|
+
end
|
19
|
+
}
|
20
|
+
|
21
|
+
its(:key) { should eq(key) }
|
22
|
+
its(:shift) { should eq(shift) }
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module ExampleHelpers
|
4
|
+
def filename_for(cipher, mode, group=nil)
|
5
|
+
_group = group ? "_#{group}" : ""
|
6
|
+
"#{cipher}_examples_#{mode}#{_group}.yaml"
|
7
|
+
end
|
8
|
+
|
9
|
+
def load_examples(cipher, mode, group)
|
10
|
+
filename = filename_for(cipher, mode, group)
|
11
|
+
path = File.join(File.dirname(__FILE__), filename)
|
12
|
+
examples = YAML.load(File.read(path)) || []
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
---
|
2
|
+
# Basic encryption examples
|
3
|
+
-
|
4
|
+
ciphertext: IS
|
5
|
+
cleartext: de
|
6
|
+
key: fortification
|
7
|
+
-
|
8
|
+
ciphertext: IS
|
9
|
+
cleartext: " d e "
|
10
|
+
key: fortification
|
11
|
+
-
|
12
|
+
ciphertext: IS
|
13
|
+
cleartext: "`~!@#$%^d&*()_-=+|]}[{e;:/?>.,<'\\\""
|
14
|
+
key: fortification
|
15
|
+
-
|
16
|
+
cleartext: "123456789"
|
17
|
+
ciphertext: ""
|
18
|
+
key: fortification
|
19
|
+
-
|
20
|
+
cleartext: ""
|
21
|
+
ciphertext: ""
|
22
|
+
key: fortification
|
23
|
+
-
|
24
|
+
cleartext: def
|
25
|
+
ciphertext: ISW
|
26
|
+
key: fortification
|
27
|
+
-
|
28
|
+
cleartext: defendtheeastwallofthecastle
|
29
|
+
ciphertext: ISWXVIBJEXIGGBOCEWKBJEVIGGQS
|
30
|
+
key: fortification
|
31
|
+
-
|
32
|
+
cleartext: "Forsaking monastic tradition, twelve jovial friars gave up their vocation for a questionable existence on the flying trapeze."
|
33
|
+
ciphertext: "KCILIPQPGFWBNXHZVBWIFIMQCAYKVEDJRQVBIZSWWRKALIXENXHUJWIOWHIVIHVTBWOHNMXBKOGIPYJSOBAYMPCXWBGMSWEGNVITKIDRES"
|
34
|
+
key: fortification
|
35
|
+
-
|
36
|
+
cleartext: "FORSAKINGMONASTICTRADITIONTWELVEJOVIALFRIARSGAVEUPTHEIRVOCATIONFORAQUESTIONABLEEXISTENCEONTHEFLYINGTRAPEZE"
|
37
|
+
ciphertext: "KCILIPQPGFWBNXHZVBWIFIMQCAYKVEDJRQVBIZSWWRKALIXENXHUJWIOWHIVIHVTBWOHNMXBKOGIPYJSOBAYMPCXWBGMSWEGNVITKIDRES"
|
38
|
+
key: fortification
|
39
|
+
|
40
|
+
# it "decrypts" do
|
41
|
+
# result = Vigenere.decrypt(@ciphertext, @key)
|
42
|
+
# expect(result).to eq(@cleartext)
|
43
|
+
# end
|
44
|
+
|
45
|
+
# it "roundtrips" do
|
46
|
+
# intermediate = Vigenere.encrypt(@cleartext, @key)
|
47
|
+
# result = Vigenere.decrypt(intermediate, @key)
|
48
|
+
# expect(result).to eq(@cleartext)
|
49
|
+
# end
|
50
|
+
|
51
|
+
# # default key = 'SUSHIFROGGY'
|
52
|
+
# describe "using the default key" do
|
53
|
+
# before do
|
54
|
+
# @ciphertext = "XIJZIPZBMSMFUKAQHKFGJGLCGUBBVZBKHGPAHTKIWGXQYUNLCUKVKOPNIUHBNFBLUPSKMLAYZCTGZDYWEQXKSTICGHLOMKCMOTELLSWMEV"
|
55
|
+
# end
|
56
|
+
|
57
|
+
# it "uses default if no key is given" do
|
58
|
+
# result = Vigenere.encrypt(@cleartext)
|
59
|
+
# expect(result).to eq(@ciphertext)
|
60
|
+
# end
|
61
|
+
|
62
|
+
# it "decrypts" do
|
63
|
+
# result = Vigenere.decrypt(@ciphertext)
|
64
|
+
# expect(result).to eq(@cleartext)
|
65
|
+
# end
|
66
|
+
|
67
|
+
# it "roundtrips" do
|
68
|
+
# intermediate = Vigenere.encrypt(@cleartext)
|
69
|
+
# result = Vigenere.decrypt(intermediate)
|
70
|
+
# expect(result).to eq(@cleartext)
|
71
|
+
# end
|
72
|
+
# end
|
73
|
+
|
74
|
+
# describe "decrypting" do
|
75
|
+
# it "decrypts 'defendtheeastwallofthecastle' to 'ISWXVIBJEXIGGBOCEWKBJEVIGGQS'" do
|
76
|
+
# cleartext = "defendtheeastwallofthecastle"
|
77
|
+
# ciphertext = "ISWXVIBJEXIGGBOCEWKBJEVIGGQS"
|
78
|
+
# result = Vigenere.decrypt(ciphertext, @key)
|
79
|
+
# expect(result).to eq(cleartext.toUpperCase())
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
# end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
---
|
2
|
+
-
|
3
|
+
ciphertext: "`~!@#$%^I&*()_-=+|]}[{S;:/?>.,<'\\\""
|
4
|
+
cleartext: "`~!@#$%^d&*()_-=+|]}[{e;:/?>.,<'\\\""
|
5
|
+
key: fortification
|
6
|
+
-
|
7
|
+
cleartext: "123456789"
|
8
|
+
ciphertext: "123456789"
|
9
|
+
key: fortification
|
10
|
+
-
|
11
|
+
cleartext: ""
|
12
|
+
ciphertext: ""
|
13
|
+
key: fortification
|
14
|
+
-
|
15
|
+
cleartext: "Forsaking monastic tradition, twelve jovial friars gave up their vocation for a questionable existence on the flying trapeze."
|
16
|
+
ciphertext: "KCILIPQPG FWBNXHZV BWIFIMQCA, YKVEDJ RQVBIZ SWWRKA LIXE NX HUJWI OWHIVIHV TBW O HNMXBKOGIPYJ SOBAYMPCX WB GMS WEGNVI TKIDRES."
|
17
|
+
key: fortification
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'wixy/vigenere'
|
2
|
+
require 'example_helper'
|
3
|
+
|
4
|
+
module ConfigHelpers
|
5
|
+
def create_config(options)
|
6
|
+
Wixy::Config.new do |config|
|
7
|
+
options.each do |key, value|
|
8
|
+
config.public_send("#{key}=", value)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
include Wixy
|
15
|
+
|
16
|
+
describe "Vigenere" do
|
17
|
+
extend ExampleHelpers
|
18
|
+
include ConfigHelpers
|
19
|
+
|
20
|
+
load_examples("vigenere", "encrypt", "discard").each do |e|
|
21
|
+
describe "'#{e['cleartext']}' -> '#{e['ciphertext']}'" do
|
22
|
+
let(:config) { create_config(key: e['key']) }
|
23
|
+
let(:cleartext) { e['cleartext'] }
|
24
|
+
let(:ciphertext) { e['ciphertext'] }
|
25
|
+
let(:vigenere) { Vigenere.new(config) }
|
26
|
+
|
27
|
+
it "encrypts" do
|
28
|
+
result = vigenere.encrypt e['cleartext']
|
29
|
+
expect(result).to eq(e['ciphertext'])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
load_examples("vigenere", "encrypt", "preserve").each do |e|
|
35
|
+
describe "'#{e['cleartext']}' -> '#{e['ciphertext']}'" do
|
36
|
+
let(:config) { create_config(key: e['key'], preserve: true) }
|
37
|
+
let(:cleartext) { e['cleartext'] }
|
38
|
+
let(:ciphertext) { e['ciphertext'] }
|
39
|
+
let(:vigenere) { Vigenere.new(config) }
|
40
|
+
|
41
|
+
it "encrypts" do
|
42
|
+
result = vigenere.encrypt e['cleartext']
|
43
|
+
expect(result).to eq(e['ciphertext'])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/spec/wixy_spec.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'wixy'
|
2
|
+
|
3
|
+
describe "Wixy" do
|
4
|
+
let(:plaintext) { 'FORSAKINGMONASTICTRADITIONTWELVEJOVIALFRIARSGAVEUPTHEIRVOCATIONFORAQUESTIONABLEEXISTENCEONTHEFLYINGTRAPEZE' }
|
5
|
+
let(:ciphertext) { 'IRUVDNLQJPRQDVWLFWUDGLWLRQWZHOYHMRYLDOIULDUVJDYHXSWKHLUYRFDWLRQIRUDTXHVWLRQDEOHHALVWHQFHRQWKHIOBLQJWUDSHCH' }
|
6
|
+
|
7
|
+
describe "as a library" do
|
8
|
+
let(:config) { Wixy::Config.new }
|
9
|
+
|
10
|
+
it "encrypts" do
|
11
|
+
result = Wixy.encrypt plaintext, config
|
12
|
+
expect(result).to eq(ciphertext)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "decrypts" do
|
16
|
+
result = Wixy.decrypt ciphertext, config
|
17
|
+
expect(result).to eq(plaintext)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "CLI" do
|
22
|
+
let(:wixy) { File.expand_path "../../bin/wixy", __FILE__ }
|
23
|
+
|
24
|
+
# Hack to help the binary find library files. When installed as a
|
25
|
+
# gem, rubygems takes care of this.
|
26
|
+
before :all do
|
27
|
+
@old_rubylib = ENV['RUBYLIB']
|
28
|
+
ENV['RUBYLIB'] = File.expand_path "../../lib", __FILE__
|
29
|
+
end
|
30
|
+
|
31
|
+
after :all do
|
32
|
+
ENV['RUBYLIB'] = @old_rubylib
|
33
|
+
end
|
34
|
+
|
35
|
+
it "encrypts" do
|
36
|
+
result = `#{wixy} #{plaintext}`.chomp
|
37
|
+
expect(result).to eq(ciphertext)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "decrypts" do
|
41
|
+
result = `#{wixy} --decrypt #{ciphertext}`.chomp
|
42
|
+
expect(result).to eq(plaintext)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wixy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Eric Watson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-09 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: guard
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.8'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.8'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: guard-rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.6'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.6'
|
62
|
+
description: ! " A library for working with classical ciphers of the\n pencil-and-paper
|
63
|
+
sort.\n"
|
64
|
+
email: wasnotrice@gmail.com
|
65
|
+
executables:
|
66
|
+
- wixy
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- lib/wixy.rb
|
71
|
+
- lib/wixy/alphabet.rb
|
72
|
+
- lib/wixy/bifid.rb
|
73
|
+
- lib/wixy/caesar.rb
|
74
|
+
- lib/wixy/config.rb
|
75
|
+
- lib/wixy/version.rb
|
76
|
+
- lib/wixy/vigenere.rb
|
77
|
+
- LICENSE
|
78
|
+
- spec/alphabet_spec.rb
|
79
|
+
- spec/bifid_spec.rb
|
80
|
+
- spec/caesar_examples_encrypt_discard.yaml
|
81
|
+
- spec/caesar_spec.rb
|
82
|
+
- spec/config_spec.rb
|
83
|
+
- spec/example_helper.rb
|
84
|
+
- spec/vigenere_examples_decrypt.yaml
|
85
|
+
- spec/vigenere_examples_encrypt_discard.yaml
|
86
|
+
- spec/vigenere_examples_encrypt_preserve.yaml
|
87
|
+
- spec/vigenere_spec.rb
|
88
|
+
- spec/wixy_spec.rb
|
89
|
+
- bin/wixy
|
90
|
+
homepage: http://rubygems.org/gems/wixy
|
91
|
+
licenses:
|
92
|
+
- MIT
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project: wixy
|
111
|
+
rubygems_version: 1.8.24
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: Classical, pencil-and-paper ciphers
|
115
|
+
test_files:
|
116
|
+
- spec/alphabet_spec.rb
|
117
|
+
- spec/bifid_spec.rb
|
118
|
+
- spec/caesar_examples_encrypt_discard.yaml
|
119
|
+
- spec/caesar_spec.rb
|
120
|
+
- spec/config_spec.rb
|
121
|
+
- spec/example_helper.rb
|
122
|
+
- spec/vigenere_examples_decrypt.yaml
|
123
|
+
- spec/vigenere_examples_encrypt_discard.yaml
|
124
|
+
- spec/vigenere_examples_encrypt_preserve.yaml
|
125
|
+
- spec/vigenere_spec.rb
|
126
|
+
- spec/wixy_spec.rb
|