shift_ciphers 0.0.1 → 0.0.2

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: 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