shift_ciphers 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +20 -20
- data/README.md +83 -69
- data/lib/shift_ciphers/alphabets.rb +11 -11
- data/lib/shift_ciphers/caesar.rb +48 -48
- data/lib/shift_ciphers/error.rb +2 -2
- data/lib/shift_ciphers/hardened_vigenere.rb +55 -0
- data/lib/shift_ciphers/version.rb +9 -9
- data/lib/shift_ciphers/vigenere.rb +76 -71
- data/lib/shift_ciphers.rb +6 -5
- data/shift_ciphers.gemspec +25 -25
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9719f1d7ca53e6f2bdfb05df4d0f43e936c572ed687e841f63052c01aa0433b
|
4
|
+
data.tar.gz: bc58897ff42d8b542c03ffcc527fbfeac1c61fcc9a5074e1cc3399ab6632ea7c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e4876ec51d94454024e8caa69e7a2e36d0cf25890c74a2ff75288be68d0ea38aebe3d5234736db32251f6fbf72653df2bfc41ad45bbea02f86a6c4b2969e237
|
7
|
+
data.tar.gz: 73bc9aed8e221b46a6186375de7b3658df0661c37494fe9c33f2bea53deb74db3f77473defb732bc056f672dfe430eba11d48f90478f1c724dbe37dc2f69733c
|
data/LICENSE
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
The MIT License (MIT)
|
2
|
-
|
3
|
-
Copyright (c) 2018 Tomasz Więch
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
7
|
-
in the Software without restriction, including without limitation the rights
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
10
|
-
furnished to do so, subject to the following conditions:
|
11
|
-
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
13
|
-
copies or substantial portions of the Software.
|
14
|
-
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Tomasz Więch
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
21
|
SOFTWARE.
|
data/README.md
CHANGED
@@ -1,69 +1,83 @@
|
|
1
|
-
Shift Ciphers [![Gem Version](https://badge.fury.io/rb/shift_ciphers.svg)](http://badge.fury.io/rb/shift_ciphers) [![Build Status](https://travis-ci.org/TeWu/shift-ciphers.svg?branch=master)](https://travis-ci.org/TeWu/shift-ciphers)
|
2
|
-
=======
|
3
|
-
|
4
|
-
**Shift Ciphers** gem is simple, yet complete, implementation of [Caesar]
|
5
|
-
|
6
|
-
Installation
|
7
|
-
-------
|
8
|
-
|
9
|
-
gem install shift_ciphers
|
10
|
-
|
11
|
-
Basic usage
|
12
|
-
-------
|
13
|
-
|
14
|
-
```ruby
|
15
|
-
require 'shift_ciphers'
|
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
|
-
|
29
|
-
|
30
|
-
```
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
decrypted
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
encrypted =
|
48
|
-
decrypted =
|
49
|
-
decrypted == plaintext # Should be true
|
50
|
-
```
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
```ruby
|
55
|
-
plaintext = "
|
56
|
-
encrypted = ShiftCiphers::Vigenere.encrypt(plaintext, "KEYWORD", alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
1
|
+
Shift Ciphers [![Gem Version](https://badge.fury.io/rb/shift_ciphers.svg)](http://badge.fury.io/rb/shift_ciphers) [![Build Status](https://travis-ci.org/TeWu/shift-ciphers.svg?branch=master)](https://travis-ci.org/TeWu/shift-ciphers)
|
2
|
+
=======
|
3
|
+
|
4
|
+
**Shift Ciphers** gem is simple, yet complete, implementation of classic [Caesar][1] and [Vigenère][2] ciphers. It also features custom, hardened version of Vigenère cipher, which uses [autokey scheme][3] and [PRNG][4]s.
|
5
|
+
|
6
|
+
Installation
|
7
|
+
-------
|
8
|
+
|
9
|
+
gem install shift_ciphers
|
10
|
+
|
11
|
+
Basic usage
|
12
|
+
-------
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
require 'shift_ciphers'
|
16
|
+
|
17
|
+
plaintext = "Attack at dawn!"
|
18
|
+
|
19
|
+
encrypted = ShiftCiphers::Caesar.encrypt(plaintext, offset: 5) # => "Fyyfhp%fy%ifBs^"
|
20
|
+
decrypted = ShiftCiphers::Caesar.decrypt(encrypted, offset: 5) # => "Attack at dawn!"
|
21
|
+
decrypted == plaintext # Should be true
|
22
|
+
|
23
|
+
encrypted = ShiftCiphers::Vigenere.encrypt(plaintext, "my keyword") # => "W!0uqS3yU=zI3H{"
|
24
|
+
decrypted = ShiftCiphers::Vigenere.decrypt(encrypted, "my keyword") # => "Attack at dawn!"
|
25
|
+
decrypted == plaintext # Should be true
|
26
|
+
|
27
|
+
encrypted = ShiftCiphers::HardenedVigenere.encrypt(plaintext, "my keyword") # => "Z6tappN^Ap[o&Ns"
|
28
|
+
decrypted = ShiftCiphers::HardenedVigenere.decrypt(encrypted, "my keyword") # => "Attack at dawn!"
|
29
|
+
decrypted == plaintext # Should be true
|
30
|
+
```
|
31
|
+
|
32
|
+
... or instantiate a cipher, and benefit from stored configuration info (e.g. `offset` for Caesar cipher, or `key` for Vigenère):
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
caesar = ShiftCiphers::Caesar.new
|
36
|
+
caesar.offset = 5
|
37
|
+
encrypted = caesar.encrypt(plaintext) # => "Fyyfhp%fy%ifBs^"
|
38
|
+
decrypted = caesar.decrypt(encrypted) # => "Attack at dawn!"
|
39
|
+
decrypted == plaintext # Should be true
|
40
|
+
|
41
|
+
vigenere = ShiftCiphers::Vigenere.new("my keyword")
|
42
|
+
encrypted = vigenere.encrypt(plaintext) # => "W!0uqS3yU=zI3H{"
|
43
|
+
decrypted = vigenere.decrypt(encrypted) # => "Attack at dawn!"
|
44
|
+
decrypted == plaintext # Should be true
|
45
|
+
|
46
|
+
strong_vigenere = ShiftCiphers::HardenedVigenere.new("my keyword")
|
47
|
+
encrypted = strong_vigenere.encrypt(plaintext) # => "Z6tappN^Ap[o&Ns"
|
48
|
+
decrypted = strong_vigenere.decrypt(encrypted) # => "Attack at dawn!"
|
49
|
+
decrypted == plaintext # Should be true
|
50
|
+
```
|
51
|
+
|
52
|
+
You can customize alphabet used by cipher:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
plaintext = "ATTACKATDAWN"
|
56
|
+
encrypted = ShiftCiphers::Vigenere.encrypt(plaintext, "KEYWORD", alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ") # => "KXRWQBDDHYSB"
|
57
|
+
decrypted = ShiftCiphers::Vigenere.decrypt(encrypted, "KEYWORD", alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ") # => "ATTACKATDAWN"
|
58
|
+
decrypted == plaintext # Should be true
|
59
|
+
```
|
60
|
+
|
61
|
+
When you attempt to encrypt a string, which contains character that is not in the cipher's alphabet, then `ShiftCiphers::CipherError` is rised:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
plaintext = "ATTACK!"
|
65
|
+
encrypted = ShiftCiphers::Vigenere.encrypt(plaintext, "KEYWORD", alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
66
|
+
# Raises ShiftCiphers::CipherError: Invalid input "ATTACK!". Character "!" is not in the alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
67
|
+
```
|
68
|
+
|
69
|
+
You can avoid this exception by telling cipher not to 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):
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
plaintext = "ATTACK AT DAWN!"
|
73
|
+
encrypted = ShiftCiphers::Vigenere.encrypt(plaintext, "KEYWORD", alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", nonalphabet_char_strategy: :dont_encrypt)
|
74
|
+
decrypted = ShiftCiphers::Vigenere.decrypt(encrypted, "KEYWORD", alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", nonalphabet_char_strategy: :dont_encrypt)
|
75
|
+
puts plaintext # => ATTACK AT DAWN!
|
76
|
+
puts encrypted # => KXRWQB DD HYSB!
|
77
|
+
decrypted == plaintext # Should be true
|
78
|
+
```
|
79
|
+
|
80
|
+
[1]: https://en.wikipedia.org/wiki/Caesar_cipher
|
81
|
+
[2]: https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher
|
82
|
+
[3]: https://en.wikipedia.org/wiki/Autokey_cipher
|
83
|
+
[4]: https://en.wikipedia.org/wiki/Pseudorandom_number_generator
|
@@ -1,12 +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
|
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
12
|
end
|
data/lib/shift_ciphers/caesar.rb
CHANGED
@@ -1,49 +1,49 @@
|
|
1
|
-
module ShiftCiphers
|
2
|
-
class Caesar
|
3
|
-
DEFAULT_OFFSET = 13
|
4
|
-
attr_accessor :offset, :alphabet, :nonalphabet_char_strategy
|
5
|
-
|
6
|
-
def initialize(offset: DEFAULT_OFFSET, alphabet: Alphabets::DEFAULT, nonalphabet_char_strategy: :error)
|
7
|
-
@offset = offset
|
8
|
-
@alphabet = alphabet
|
9
|
-
@nonalphabet_char_strategy = nonalphabet_char_strategy
|
10
|
-
end
|
11
|
-
|
12
|
-
def encrypt(plaintext)
|
13
|
-
process(plaintext,
|
14
|
-
end
|
15
|
-
|
16
|
-
def decrypt(ciphertext)
|
17
|
-
process(ciphertext,
|
18
|
-
end
|
19
|
-
|
20
|
-
protected
|
21
|
-
|
22
|
-
def process(text,
|
23
|
-
text.each_char.reduce("") do |
|
24
|
-
char_idx = alphabet.index(char)
|
25
|
-
if !char_idx.nil?
|
26
|
-
rel_offset = offset * (
|
27
|
-
|
28
|
-
else
|
29
|
-
if nonalphabet_char_strategy == :dont_encrypt
|
30
|
-
|
31
|
-
else
|
32
|
-
raise CipherError.new("Character #{char.inspect} is not in the alphabet: #{alphabet.inspect}")
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
class << self
|
39
|
-
def encrypt(plaintext, **options)
|
40
|
-
self.new(**options).encrypt(plaintext)
|
41
|
-
end
|
42
|
-
|
43
|
-
def decrypt(ciphertext, **options)
|
44
|
-
self.new(**options).decrypt(ciphertext)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
1
|
+
module ShiftCiphers
|
2
|
+
class Caesar
|
3
|
+
DEFAULT_OFFSET = 13
|
4
|
+
attr_accessor :offset, :alphabet, :nonalphabet_char_strategy
|
5
|
+
|
6
|
+
def initialize(offset: DEFAULT_OFFSET, alphabet: Alphabets::DEFAULT, nonalphabet_char_strategy: :error)
|
7
|
+
@offset = offset
|
8
|
+
@alphabet = alphabet
|
9
|
+
@nonalphabet_char_strategy = nonalphabet_char_strategy
|
10
|
+
end
|
11
|
+
|
12
|
+
def encrypt(plaintext)
|
13
|
+
process(plaintext, true)
|
14
|
+
end
|
15
|
+
|
16
|
+
def decrypt(ciphertext)
|
17
|
+
process(ciphertext, false)
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def process(text, encrypting = true)
|
23
|
+
text.each_char.reduce("") do |result, char|
|
24
|
+
char_idx = alphabet.index(char)
|
25
|
+
if !char_idx.nil?
|
26
|
+
rel_offset = offset * (encrypting ? 1 : -1)
|
27
|
+
result << alphabet[(char_idx + rel_offset) % alphabet.size]
|
28
|
+
else
|
29
|
+
if nonalphabet_char_strategy == :dont_encrypt
|
30
|
+
result << char
|
31
|
+
else
|
32
|
+
raise CipherError.new("Character #{char.inspect} is not in the alphabet: #{alphabet.inspect}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
def encrypt(plaintext, **options)
|
40
|
+
self.new(**options).encrypt(plaintext)
|
41
|
+
end
|
42
|
+
|
43
|
+
def decrypt(ciphertext, **options)
|
44
|
+
self.new(**options).decrypt(ciphertext)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
49
|
end
|
data/lib/shift_ciphers/error.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module ShiftCiphers
|
2
|
-
class CipherError < StandardError; end
|
1
|
+
module ShiftCiphers
|
2
|
+
class CipherError < StandardError; end
|
3
3
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module ShiftCiphers
|
2
|
+
class HardenedVigenere < Vigenere
|
3
|
+
|
4
|
+
def initialize(key, alphabet: Alphabets::DEFAULT, nonalphabet_char_strategy: :error)
|
5
|
+
validate_key(key, alphabet)
|
6
|
+
@key = key
|
7
|
+
@alphabet = alphabet
|
8
|
+
@nonalphabet_char_strategy = nonalphabet_char_strategy
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def process(text, encrypting = true)
|
14
|
+
offsets_stream = create_offsets_stream
|
15
|
+
plaintext_char = ""
|
16
|
+
text.each_char.reduce("") do |result, char|
|
17
|
+
char_idx = alphabet.index(char)
|
18
|
+
if !char_idx.nil?
|
19
|
+
rel_offset = offsets_stream.next(plaintext_char) * (encrypting ? 1 : -1)
|
20
|
+
result_char = alphabet[(char_idx + rel_offset) % alphabet.size]
|
21
|
+
plaintext_char = encrypting ? char : result_char
|
22
|
+
result << result_char
|
23
|
+
else
|
24
|
+
if nonalphabet_char_strategy == :dont_encrypt
|
25
|
+
result << char
|
26
|
+
else
|
27
|
+
raise CipherError.new("Invalid input #{text.inspect}. Character #{char.inspect} is not in the alphabet: #{alphabet.inspect}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_offsets_stream
|
34
|
+
RandomOffsetsStream.new(key, alphabet.size - 1)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
class RandomOffsetsStream
|
39
|
+
SEEDING_RAND_MAX = 2**32-1
|
40
|
+
|
41
|
+
def initialize(key, max)
|
42
|
+
@random = key.bytes.reduce(Random.new(0)) do |random, byte|
|
43
|
+
Random.new(random.rand(SEEDING_RAND_MAX) ^ byte)
|
44
|
+
end
|
45
|
+
@max = max
|
46
|
+
end
|
47
|
+
|
48
|
+
def next(plaintext_char)
|
49
|
+
plaintext_byte = plaintext_char.bytes.reduce(0){|a,e| a ^ e}
|
50
|
+
@random = Random.new(@random.rand(SEEDING_RAND_MAX) ^ plaintext_byte)
|
51
|
+
@random.rand(@max)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
module ShiftCiphers
|
2
|
-
module Version
|
3
|
-
MAJOR = 0
|
4
|
-
MINOR = 0
|
5
|
-
PATCH =
|
6
|
-
LABEL = nil
|
7
|
-
end
|
8
|
-
|
9
|
-
VERSION = ([Version::MAJOR, Version::MINOR, Version::PATCH, Version::LABEL].compact * '.').freeze
|
1
|
+
module ShiftCiphers
|
2
|
+
module Version
|
3
|
+
MAJOR = 0
|
4
|
+
MINOR = 0
|
5
|
+
PATCH = 3
|
6
|
+
LABEL = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
VERSION = ([Version::MAJOR, Version::MINOR, Version::PATCH, Version::LABEL].compact * '.').freeze
|
10
10
|
end
|
@@ -1,72 +1,77 @@
|
|
1
|
-
module ShiftCiphers
|
2
|
-
class Vigenere
|
3
|
-
attr_accessor :key, :alphabet, :nonalphabet_char_strategy
|
4
|
-
|
5
|
-
def initialize(key, alphabet: Alphabets::DEFAULT, nonalphabet_char_strategy: :error)
|
6
|
-
validate_key(key, alphabet)
|
7
|
-
@key = key
|
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
|
22
|
-
end
|
23
|
-
|
24
|
-
def encrypt(plaintext)
|
25
|
-
process(plaintext,
|
26
|
-
end
|
27
|
-
|
28
|
-
def decrypt(ciphertext)
|
29
|
-
process(ciphertext,
|
30
|
-
end
|
31
|
-
|
32
|
-
protected
|
33
|
-
|
34
|
-
def process(text,
|
35
|
-
|
36
|
-
text.each_char.reduce("") do |
|
37
|
-
char_idx = alphabet.index(char)
|
38
|
-
if !char_idx.nil?
|
39
|
-
rel_offset =
|
40
|
-
|
41
|
-
else
|
42
|
-
if nonalphabet_char_strategy == :dont_encrypt
|
43
|
-
|
44
|
-
else
|
45
|
-
raise CipherError.new("Invalid input #{text.inspect}. Character #{char.inspect} is not in the alphabet: #{alphabet.inspect}")
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def
|
52
|
-
key.
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
1
|
+
module ShiftCiphers
|
2
|
+
class Vigenere
|
3
|
+
attr_accessor :key, :alphabet, :nonalphabet_char_strategy
|
4
|
+
|
5
|
+
def initialize(key, alphabet: Alphabets::DEFAULT, nonalphabet_char_strategy: :error)
|
6
|
+
validate_key(key, alphabet)
|
7
|
+
@key = key
|
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
|
22
|
+
end
|
23
|
+
|
24
|
+
def encrypt(plaintext)
|
25
|
+
process(plaintext, true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def decrypt(ciphertext)
|
29
|
+
process(ciphertext, false)
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def process(text, encrypting = true)
|
35
|
+
offsets_stream = create_offsets_stream
|
36
|
+
text.each_char.reduce("") do |result, char|
|
37
|
+
char_idx = alphabet.index(char)
|
38
|
+
if !char_idx.nil?
|
39
|
+
rel_offset = offsets_stream.next * (encrypting ? 1 : -1)
|
40
|
+
result << alphabet[(char_idx + rel_offset) % alphabet.size]
|
41
|
+
else
|
42
|
+
if nonalphabet_char_strategy == :dont_encrypt
|
43
|
+
result << 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
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_key_offsets
|
52
|
+
@key_offsets = @key.chars.map {|c| alphabet.index(c) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_offsets_stream
|
56
|
+
@key_offsets.cycle
|
57
|
+
end
|
58
|
+
|
59
|
+
def validate_key(key, alphabet)
|
60
|
+
key.each_char do |char|
|
61
|
+
raise CipherError.new("Invalid key #{key.inspect}. Character #{char.inspect} is not in the alphabet: #{alphabet.inspect}") unless alphabet.include?(char)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
class << self
|
67
|
+
def encrypt(plaintext, key, **options)
|
68
|
+
self.new(key, **options).encrypt(plaintext)
|
69
|
+
end
|
70
|
+
|
71
|
+
def decrypt(ciphertext, key, **options)
|
72
|
+
self.new(key, **options).decrypt(ciphertext)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
72
77
|
end
|
data/lib/shift_ciphers.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require 'shift_ciphers/version'
|
2
|
-
require 'shift_ciphers/alphabets'
|
3
|
-
require 'shift_ciphers/error'
|
4
|
-
require 'shift_ciphers/caesar'
|
5
|
-
require 'shift_ciphers/vigenere'
|
1
|
+
require 'shift_ciphers/version'
|
2
|
+
require 'shift_ciphers/alphabets'
|
3
|
+
require 'shift_ciphers/error'
|
4
|
+
require 'shift_ciphers/caesar'
|
5
|
+
require 'shift_ciphers/vigenere'
|
6
|
+
require 'shift_ciphers/hardened_vigenere'
|
data/shift_ciphers.gemspec
CHANGED
@@ -1,26 +1,26 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
lib = File.expand_path('../lib', __FILE__)
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require 'shift_ciphers/version'
|
6
|
-
|
7
|
-
Gem::Specification.new do |s|
|
8
|
-
s.name = "shift_ciphers"
|
9
|
-
s.version = ShiftCiphers::VERSION
|
10
|
-
s.authors = ["Tomasz Więch"]
|
11
|
-
s.email = ["tewu.dev@gmail.com"]
|
12
|
-
|
13
|
-
s.summary = "Implementation of Caesar and Vigenere ciphers"
|
14
|
-
s.description = "Implementation of Caesar and Vigenere ciphers"
|
15
|
-
s.homepage = "https://github.com/TeWu/shift-ciphers"
|
16
|
-
s.license = "MIT"
|
17
|
-
|
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
|
-
s.require_paths = ["lib"]
|
20
|
-
|
21
|
-
s.required_ruby_version = ">= 2.1.0"
|
22
|
-
|
23
|
-
s.add_development_dependency "bundler", "~> 1.16"
|
24
|
-
s.add_development_dependency "pry", "~> 0.11"
|
25
|
-
s.add_development_dependency "rspec", "~> 3.8"
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'shift_ciphers/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "shift_ciphers"
|
9
|
+
s.version = ShiftCiphers::VERSION
|
10
|
+
s.authors = ["Tomasz Więch"]
|
11
|
+
s.email = ["tewu.dev@gmail.com"]
|
12
|
+
|
13
|
+
s.summary = "Implementation of Caesar and Vigenere ciphers"
|
14
|
+
s.description = "Implementation of Caesar and Vigenere ciphers"
|
15
|
+
s.homepage = "https://github.com/TeWu/shift-ciphers"
|
16
|
+
s.license = "MIT"
|
17
|
+
|
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
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.required_ruby_version = ">= 2.1.0"
|
22
|
+
|
23
|
+
s.add_development_dependency "bundler", "~> 1.16"
|
24
|
+
s.add_development_dependency "pry", "~> 0.11"
|
25
|
+
s.add_development_dependency "rspec", "~> 3.8"
|
26
26
|
end
|
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.3
|
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-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -65,6 +65,7 @@ files:
|
|
65
65
|
- lib/shift_ciphers/alphabets.rb
|
66
66
|
- lib/shift_ciphers/caesar.rb
|
67
67
|
- lib/shift_ciphers/error.rb
|
68
|
+
- lib/shift_ciphers/hardened_vigenere.rb
|
68
69
|
- lib/shift_ciphers/version.rb
|
69
70
|
- lib/shift_ciphers/vigenere.rb
|
70
71
|
- shift_ciphers.gemspec
|