shift_ciphers 0.0.1 → 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.
- checksums.yaml +4 -4
- data/README.md +52 -10
- data/lib/shift_ciphers/alphabets.rb +12 -0
- data/lib/shift_ciphers/caesar.rb +28 -20
- data/lib/shift_ciphers/error.rb +3 -0
- data/lib/shift_ciphers/version.rb +1 -1
- data/lib/shift_ciphers/vigenere.rb +51 -20
- data/lib/shift_ciphers.rb +2 -0
- data/shift_ciphers.gemspec +1 -1
- metadata +5 -4
- data/lib/shift_ciphers/shift_ciphers.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7339c2a602c6851a97eb2ae8e9a8fb0bba7acedf908cf9bdfa44b10258b6b52f
|
4
|
+
data.tar.gz: dab0336c772360bdd50888e7aae4c26215f9fc8d423e1b6086322debd90f9334
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca83d8402f7f5e9365e54fbf5b8c01d5557603701a8a9afae17f941dcbfc68f204079d391763c9d084db4e7d9c3886b4fde1e53de4f0ca6d63984cd649647ec6
|
7
|
+
data.tar.gz: 5b6486223be628db1143c7b3864f510f39902cbb803a688c7891cd1933f86dffdc2c77af63654df2e827452e963ad7fa260b6ae58a69f43ba7ca8de7cd6dda6e
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Shift Ciphers [](http://badge.fury.io/rb/shift_ciphers) [](http://badge.fury.io/rb/shift_ciphers) [](https://travis-ci.org/TeWu/shift-ciphers)
|
2
2
|
=======
|
3
3
|
|
4
4
|
**Shift Ciphers** gem is simple, yet complete, implementation of [Caesar](https://en.wikipedia.org/wiki/Caesar_cipher) and [Vigenère](https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher) ciphers.
|
@@ -14,14 +14,56 @@ Basic usage
|
|
14
14
|
```ruby
|
15
15
|
require 'shift_ciphers'
|
16
16
|
|
17
|
+
plaintext = "Attack at dawn!"
|
18
|
+
|
19
|
+
encrypted = ShiftCiphers::Caesar.encrypt(plaintext, offset: 5)
|
20
|
+
decrypted = ShiftCiphers::Caesar.decrypt(encrypted, offset: 5)
|
21
|
+
decrypted == plaintext # Should be true
|
22
|
+
|
23
|
+
encrypted = ShiftCiphers::Vigenere.encrypt(plaintext, "my keyword")
|
24
|
+
decrypted = ShiftCiphers::Vigenere.decrypt(encrypted, "my keyword")
|
25
|
+
decrypted == plaintext # Should be true
|
26
|
+
```
|
27
|
+
|
28
|
+
... or instantiate cipher, and benefit from stored configuration info (e.g. `offset` for Caesar cipher, or `key` for Vigenère cipher):
|
29
|
+
|
30
|
+
```ruby
|
17
31
|
caesar = ShiftCiphers::Caesar.new
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
32
|
+
caesar.offset = 5
|
33
|
+
encrypted = caesar.encrypt(plaintext)
|
34
|
+
decrypted = caesar.decrypt(encrypted)
|
35
|
+
decrypted == plaintext # Should be true
|
36
|
+
|
37
|
+
vigenere = ShiftCiphers::Vigenere.new("my keyword")
|
38
|
+
encrypted = vigenere.encrypt(plaintext)
|
39
|
+
decrypted = vigenere.decrypt(encrypted)
|
40
|
+
decrypted == plaintext # Should be true
|
41
|
+
```
|
42
|
+
|
43
|
+
You can customize alphabet used by cipher:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
plaintext = "ATTACKATDAWN"
|
47
|
+
encrypted = ShiftCiphers::Vigenere.encrypt(plaintext, "KEYWORD", alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
48
|
+
decrypted = ShiftCiphers::Vigenere.decrypt(encrypted, "KEYWORD", alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
49
|
+
decrypted == plaintext # Should be true
|
27
50
|
```
|
51
|
+
|
52
|
+
When you attempt to encrypt a string, which contains character that is not in the cipher's alphabet, then `ShiftCiphers::CipherError` is rised:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
plaintext = "ATTACK!"
|
56
|
+
encrypted = ShiftCiphers::Vigenere.encrypt(plaintext, "KEYWORD", alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
57
|
+
# Raises ShiftCiphers::CipherError: Invalid input "ATTACK!". Character "!" is not in the alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
58
|
+
```
|
59
|
+
|
60
|
+
You can avoid this exception by telling cipher to not encrypt characters which are not in its alphabet. This is done by passing `nonalphabet_char_strategy` argument to `encrypt`/`decrypt` class method (or by using `nonalphabet_char_strategy=` instance method):
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
plaintext = "ATTACK AT DAWN!"
|
64
|
+
encrypted = ShiftCiphers::Vigenere.encrypt(plaintext, "KEYWORD", alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", nonalphabet_char_strategy: :dont_encrypt)
|
65
|
+
decrypted = ShiftCiphers::Vigenere.decrypt(encrypted, "KEYWORD", alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", nonalphabet_char_strategy: :dont_encrypt)
|
66
|
+
puts plaintext # => ATTACK AT DAWN!
|
67
|
+
puts encrypted # => KXRWQB DD HYSB!
|
68
|
+
decrypted == plaintext # Should be true
|
69
|
+
```
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ShiftCiphers
|
2
|
+
module Alphabets
|
3
|
+
NUMS = (0..9).to_a.map(&:to_s).join
|
4
|
+
LOWER_ALPHA = ('a'..'z').to_a.join
|
5
|
+
UPPER_ALPHA = ('A'..'Z').to_a.join
|
6
|
+
ALPHA = LOWER_ALPHA + UPPER_ALPHA
|
7
|
+
ALPHANUMS = NUMS + ALPHA
|
8
|
+
SPECIAL = " !@#$%^&*()-_=+{}[];:'\",./<>?"
|
9
|
+
|
10
|
+
DEFAULT = ALPHANUMS + SPECIAL
|
11
|
+
end
|
12
|
+
end
|
data/lib/shift_ciphers/caesar.rb
CHANGED
@@ -1,41 +1,49 @@
|
|
1
|
-
require 'shift_ciphers/shift_ciphers'
|
2
|
-
|
3
1
|
module ShiftCiphers
|
4
2
|
class Caesar
|
5
3
|
DEFAULT_OFFSET = 13
|
6
|
-
attr_accessor :offset, :alphabet
|
4
|
+
attr_accessor :offset, :alphabet, :nonalphabet_char_strategy
|
7
5
|
|
8
|
-
def initialize(offset
|
6
|
+
def initialize(offset: DEFAULT_OFFSET, alphabet: Alphabets::DEFAULT, nonalphabet_char_strategy: :error)
|
9
7
|
@offset = offset
|
10
8
|
@alphabet = alphabet
|
9
|
+
@nonalphabet_char_strategy = nonalphabet_char_strategy
|
11
10
|
end
|
12
11
|
|
13
12
|
def encrypt(plaintext)
|
14
|
-
|
13
|
+
process(plaintext, :encrypt)
|
15
14
|
end
|
16
15
|
|
17
|
-
def decrypt(
|
18
|
-
|
16
|
+
def decrypt(ciphertext)
|
17
|
+
process(ciphertext, :decrypt)
|
19
18
|
end
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
protected
|
21
|
+
|
22
|
+
def process(text, direction)
|
23
|
+
text.each_char.reduce("") do |ciphertext, char|
|
24
|
+
char_idx = alphabet.index(char)
|
25
|
+
if !char_idx.nil?
|
26
|
+
rel_offset = offset * (direction == :encrypt ? 1 : -1)
|
27
|
+
ciphertext << alphabet[(char_idx + rel_offset) % alphabet.size]
|
28
|
+
else
|
29
|
+
if nonalphabet_char_strategy == :dont_encrypt
|
30
|
+
ciphertext << char
|
31
|
+
else
|
32
|
+
raise CipherError.new("Character #{char.inspect} is not in the alphabet: #{alphabet.inspect}")
|
33
|
+
end
|
34
|
+
end
|
24
35
|
end
|
36
|
+
end
|
25
37
|
|
26
|
-
|
27
|
-
|
38
|
+
class << self
|
39
|
+
def encrypt(plaintext, **options)
|
40
|
+
self.new(**options).encrypt(plaintext)
|
28
41
|
end
|
29
42
|
|
30
|
-
|
31
|
-
|
32
|
-
def process(text, offset, direction = :encrypt, alphabet = ALPHABET)
|
33
|
-
offset *= (direction == :encrypt ? 1 : -1)
|
34
|
-
text.each_char.reduce("") do |cyphertext, char|
|
35
|
-
char_idx = alphabet.index(char)
|
36
|
-
cyphertext << alphabet[(char_idx + offset) % alphabet.size]
|
37
|
-
end
|
43
|
+
def decrypt(ciphertext, **options)
|
44
|
+
self.new(**options).decrypt(ciphertext)
|
38
45
|
end
|
39
46
|
end
|
47
|
+
|
40
48
|
end
|
41
49
|
end
|
@@ -1,41 +1,72 @@
|
|
1
|
-
require 'shift_ciphers/shift_ciphers'
|
2
|
-
|
3
1
|
module ShiftCiphers
|
4
2
|
class Vigenere
|
5
|
-
attr_accessor :key, :alphabet
|
3
|
+
attr_accessor :key, :alphabet, :nonalphabet_char_strategy
|
6
4
|
|
7
|
-
def initialize(key, alphabet
|
5
|
+
def initialize(key, alphabet: Alphabets::DEFAULT, nonalphabet_char_strategy: :error)
|
6
|
+
validate_key(key, alphabet)
|
8
7
|
@key = key
|
9
8
|
@alphabet = alphabet
|
9
|
+
@nonalphabet_char_strategy = nonalphabet_char_strategy
|
10
|
+
set_key_offsets
|
11
|
+
end
|
12
|
+
|
13
|
+
def key=(key)
|
14
|
+
validate_key(key, alphabet)
|
15
|
+
@key = key
|
16
|
+
set_key_offsets
|
17
|
+
end
|
18
|
+
|
19
|
+
def alphabet=(alphabet)
|
20
|
+
validate_key(key, alphabet)
|
21
|
+
@alphabet = alphabet
|
10
22
|
end
|
11
23
|
|
12
24
|
def encrypt(plaintext)
|
13
|
-
|
25
|
+
process(plaintext, :encrypt)
|
14
26
|
end
|
15
27
|
|
16
|
-
def decrypt(
|
17
|
-
|
28
|
+
def decrypt(ciphertext)
|
29
|
+
process(ciphertext, :decrypt)
|
18
30
|
end
|
19
31
|
|
20
|
-
|
21
|
-
|
22
|
-
|
32
|
+
protected
|
33
|
+
|
34
|
+
def process(text, direction)
|
35
|
+
key_offsets = @key_offsets.cycle
|
36
|
+
text.each_char.reduce("") do |ciphertext, char|
|
37
|
+
char_idx = alphabet.index(char)
|
38
|
+
if !char_idx.nil?
|
39
|
+
rel_offset = key_offsets.next * (direction == :encrypt ? 1 : -1)
|
40
|
+
ciphertext << alphabet[(char_idx + rel_offset) % alphabet.size]
|
41
|
+
else
|
42
|
+
if nonalphabet_char_strategy == :dont_encrypt
|
43
|
+
ciphertext << char
|
44
|
+
else
|
45
|
+
raise CipherError.new("Invalid input #{text.inspect}. Character #{char.inspect} is not in the alphabet: #{alphabet.inspect}")
|
46
|
+
end
|
47
|
+
end
|
23
48
|
end
|
49
|
+
end
|
24
50
|
|
25
|
-
|
26
|
-
|
51
|
+
def validate_key(key, alphabet)
|
52
|
+
key.each_char do |char|
|
53
|
+
raise CipherError.new("Invalid key #{key.inspect}. Character #{char.inspect} is not in the alphabet: #{alphabet.inspect}") unless alphabet.include?(char)
|
27
54
|
end
|
55
|
+
end
|
28
56
|
|
29
|
-
|
57
|
+
def set_key_offsets
|
58
|
+
@key_offsets = @key.chars.map {|c| alphabet.index(c) }
|
59
|
+
end
|
30
60
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
61
|
+
class << self
|
62
|
+
def encrypt(plaintext, key, **options)
|
63
|
+
self.new(key, **options).encrypt(plaintext)
|
64
|
+
end
|
65
|
+
|
66
|
+
def decrypt(ciphertext, key, **options)
|
67
|
+
self.new(key, **options).decrypt(ciphertext)
|
38
68
|
end
|
39
69
|
end
|
70
|
+
|
40
71
|
end
|
41
72
|
end
|
data/lib/shift_ciphers.rb
CHANGED
data/shift_ciphers.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(doc|test|spec|features|bin)/|Rakefile|Gemfile*|CHANGELOG}) || f.start_with?('.') }
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
|
-
s.required_ruby_version = ">= 2.
|
21
|
+
s.required_ruby_version = ">= 2.1.0"
|
22
22
|
|
23
23
|
s.add_development_dependency "bundler", "~> 1.16"
|
24
24
|
s.add_development_dependency "pry", "~> 0.11"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shift_ciphers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomasz Więch
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-10-
|
11
|
+
date: 2018-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -62,8 +62,9 @@ files:
|
|
62
62
|
- LICENSE
|
63
63
|
- README.md
|
64
64
|
- lib/shift_ciphers.rb
|
65
|
+
- lib/shift_ciphers/alphabets.rb
|
65
66
|
- lib/shift_ciphers/caesar.rb
|
66
|
-
- lib/shift_ciphers/
|
67
|
+
- lib/shift_ciphers/error.rb
|
67
68
|
- lib/shift_ciphers/version.rb
|
68
69
|
- lib/shift_ciphers/vigenere.rb
|
69
70
|
- shift_ciphers.gemspec
|
@@ -79,7 +80,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
80
|
requirements:
|
80
81
|
- - ">="
|
81
82
|
- !ruby/object:Gem::Version
|
82
|
-
version: 2.
|
83
|
+
version: 2.1.0
|
83
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
85
|
requirements:
|
85
86
|
- - ">="
|