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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7339c2a602c6851a97eb2ae8e9a8fb0bba7acedf908cf9bdfa44b10258b6b52f
4
- data.tar.gz: dab0336c772360bdd50888e7aae4c26215f9fc8d423e1b6086322debd90f9334
3
+ metadata.gz: f9719f1d7ca53e6f2bdfb05df4d0f43e936c572ed687e841f63052c01aa0433b
4
+ data.tar.gz: bc58897ff42d8b542c03ffcc527fbfeac1c61fcc9a5074e1cc3399ab6632ea7c
5
5
  SHA512:
6
- metadata.gz: ca83d8402f7f5e9365e54fbf5b8c01d5557603701a8a9afae17f941dcbfc68f204079d391763c9d084db4e7d9c3886b4fde1e53de4f0ca6d63984cd649647ec6
7
- data.tar.gz: 5b6486223be628db1143c7b3864f510f39902cbb803a688c7891cd1933f86dffdc2c77af63654df2e827452e963ad7fa260b6ae58a69f43ba7ca8de7cd6dda6e
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](https://en.wikipedia.org/wiki/Caesar_cipher) and [Vigenère](https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher) ciphers.
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
- ... or instantiate cipher, and benefit from stored configuration info (e.g. `offset` for Caesar cipher, or `key` for Vigenère cipher):
29
-
30
- ```ruby
31
- caesar = ShiftCiphers::Caesar.new
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
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
- ```
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
@@ -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, :encrypt)
14
- end
15
-
16
- def decrypt(ciphertext)
17
- process(ciphertext, :decrypt)
18
- end
19
-
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
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
@@ -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 = 2
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, :encrypt)
26
- end
27
-
28
- def decrypt(ciphertext)
29
- process(ciphertext, :decrypt)
30
- end
31
-
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
48
- end
49
- end
50
-
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)
54
- end
55
- end
56
-
57
- def set_key_offsets
58
- @key_offsets = @key.chars.map {|c| alphabet.index(c) }
59
- end
60
-
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)
68
- end
69
- end
70
-
71
- end
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'
@@ -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.2
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-10 00:00:00.000000000 Z
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