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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f59faf5d08db9b955f06c617b9b0191523bed8039b6c1a03f822cdcd0129c580
4
- data.tar.gz: d50f539e74132d01760f19cd6a7a0982f779f166e6d165070f03637528ce7986
3
+ metadata.gz: 7339c2a602c6851a97eb2ae8e9a8fb0bba7acedf908cf9bdfa44b10258b6b52f
4
+ data.tar.gz: dab0336c772360bdd50888e7aae4c26215f9fc8d423e1b6086322debd90f9334
5
5
  SHA512:
6
- metadata.gz: 31cbc305b50e2010c10c0581e8ed8f04733558479fdcdf199349395aa7ec479f22bd3d5d3c71a51daf15a023828b2514c678d47cd6c50b78981c6ec8734a62a5
7
- data.tar.gz: ce84442a71e158009e8e85cd9e65c854da49ca959299eb6dc6bc47b2d05fe54886a90d4ba50cfbce83a5e5057b76de5770d1beae678ec7c73671f89acc735434
6
+ metadata.gz: ca83d8402f7f5e9365e54fbf5b8c01d5557603701a8a9afae17f941dcbfc68f204079d391763c9d084db4e7d9c3886b4fde1e53de4f0ca6d63984cd649647ec6
7
+ data.tar.gz: 5b6486223be628db1143c7b3864f510f39902cbb803a688c7891cd1933f86dffdc2c77af63654df2e827452e963ad7fa260b6ae58a69f43ba7ca8de7cd6dda6e
data/README.md CHANGED
@@ -1,4 +1,4 @@
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)
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
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
- caesar_cyphertext = caesar.encrypt("ATTACKATDAWN")
19
- p caesar_cyphertext
20
- p caesar.decrypt(caesar_cyphertext)
21
-
22
- key = "5ecr3t"
23
- vigenere_cyphertext = ShiftCiphers::Vigenere.encrypt("ATTACKATDAWN", key)
24
- p key
25
- p vigenere_cyphertext
26
- p ShiftCiphers::Vigenere.decrypt(vigenere_cyphertext, key)
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
@@ -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 = DEFAULT_OFFSET, alphabet = ALPHABET)
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
- self.class.encrypt(plaintext, offset, alphabet)
13
+ process(plaintext, :encrypt)
15
14
  end
16
15
 
17
- def decrypt(cyphertext)
18
- self.class.decrypt(cyphertext, offset, alphabet)
16
+ def decrypt(ciphertext)
17
+ process(ciphertext, :decrypt)
19
18
  end
20
19
 
21
- class << self
22
- def encrypt(plaintext, offset = DEFAULT_OFFSET, alphabet = ALPHABET)
23
- process(plaintext, offset, :encrypt, alphabet)
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
- def decrypt(cyphertext, offset = DEFAULT_OFFSET, alphabet = ALPHABET)
27
- process(cyphertext, offset, :decrypt, alphabet)
38
+ class << self
39
+ def encrypt(plaintext, **options)
40
+ self.new(**options).encrypt(plaintext)
28
41
  end
29
42
 
30
- protected
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
@@ -0,0 +1,3 @@
1
+ module ShiftCiphers
2
+ class CipherError < StandardError; end
3
+ end
@@ -2,7 +2,7 @@ module ShiftCiphers
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- PATCH = 1
5
+ PATCH = 2
6
6
  LABEL = nil
7
7
  end
8
8
 
@@ -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 = 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
- self.class.encrypt(plaintext, key, alphabet)
25
+ process(plaintext, :encrypt)
14
26
  end
15
27
 
16
- def decrypt(cyphertext)
17
- self.class.decrypt(cyphertext, key, alphabet)
28
+ def decrypt(ciphertext)
29
+ process(ciphertext, :decrypt)
18
30
  end
19
31
 
20
- class << self
21
- def encrypt(plaintext, key, alphabet = ALPHABET)
22
- process(plaintext, key, :encrypt, alphabet)
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
- def decrypt(cyphertext, key, alphabet = ALPHABET)
26
- process(cyphertext, key, :decrypt, alphabet)
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
- protected
57
+ def set_key_offsets
58
+ @key_offsets = @key.chars.map {|c| alphabet.index(c) }
59
+ end
30
60
 
31
- def process(text, key, direction = :encrypt, alphabet = ALPHABET)
32
- key_chars = key.chars.cycle
33
- text.each_char.reduce("") do |cyphertext, char|
34
- char_idx = alphabet.index(char)
35
- offset = alphabet.index(key_chars.next) * (direction == :encrypt ? 1 : -1)
36
- cyphertext << alphabet[(char_idx + offset) % alphabet.size]
37
- end
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
@@ -1,3 +1,5 @@
1
1
  require 'shift_ciphers/version'
2
+ require 'shift_ciphers/alphabets'
3
+ require 'shift_ciphers/error'
2
4
  require 'shift_ciphers/caesar'
3
5
  require 'shift_ciphers/vigenere'
@@ -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.5.0"
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.1
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-06 00:00:00.000000000 Z
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/shift_ciphers.rb
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.5.0
83
+ version: 2.1.0
83
84
  required_rubygems_version: !ruby/object:Gem::Requirement
84
85
  requirements:
85
86
  - - ">="
@@ -1,6 +0,0 @@
1
- module ShiftCiphers
2
- ALPHABET = ((0..9).to_a.map(&:to_s) +
3
- ('a'..'z').to_a +
4
- ('A'..'Z').to_a
5
- ).join + " !@#$%^&*()-_=+{}[];:'\",./<>?"
6
- end