symmetric-encryption 3.7.2 → 3.8.0

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
  SHA1:
3
- metadata.gz: f92b1092b1192c42eb9b82d2ae059c548dbbe12d
4
- data.tar.gz: dd540914e52a9a772bb860c9042d98c18e7eecaf
3
+ metadata.gz: 6bf012ca0a4d3ce067a363f363168e4fcc5bebf9
4
+ data.tar.gz: 005d3a55560d79f658464ba189957f5349d221e2
5
5
  SHA512:
6
- metadata.gz: 54ff672e35c0d2bbe1545225e9da85294e032c5a0f8d211a74386659e7a9bafd354f1d4e8f64c922e6779ec0a51283a939303b7cc15c3ef27db52edd4c63e844
7
- data.tar.gz: 5d1794d4203b3a1d951479c9be6d0802bb268ee8f4ce8cc795c215b35bae57b5a7ec8a5beddefd6b1ac17a007af8894f2b9f276402bb9c48fa115236043ef463
6
+ metadata.gz: 243de98a4079b584cd215f81f15caf4e6d6985047804a6e74dbcd46e25dbfd45a7f2a3e5d6610c47309a54b0845f6bc10bbb2d7cdd9ec8ac05e5b81361335647
7
+ data.tar.gz: 56b65cf943afec131341309628ff39101be17321b194f0136cf593d67030df52b41ea1a91abcc445805c3dd2882c5802bb5a84376e352f6282d1f415342156bc
data/README.md CHANGED
@@ -1,16 +1,18 @@
1
- symmetric-encryption [![Build Status](https://secure.travis-ci.org/reidmorrison/symmetric-encryption.png?branch=master)](http://travis-ci.org/reidmorrison/symmetric-encryption) ![](http://ruby-gem-downloads-badge.herokuapp.com/symmetric-encryption?type=total)
2
- ====================
1
+ # symmetric-encryption
2
+ ![](https://img.shields.io/gem/v/symmetric-encryption.svg) ![](https://secure.travis-ci.org/reidmorrison/symmetric-encryption.png?branch=master) ![](https://img.shields.io/gem/dt/symmetric-encryption.svg) ![](https://img.shields.io/badge/status-production%20ready-blue.svg)
3
3
 
4
4
  * http://github.com/reidmorrison/symmetric-encryption
5
5
 
6
+ Transparently encrypt ActiveRecord, Mongoid, and MongoMapper attributes. Encrypt passwords in configuration files. Encrypt entire files at rest.
7
+
6
8
  ## Introduction
7
9
 
8
10
  Any project that wants to meet PCI compliance has to ensure that the data is encrypted
9
- whilst in flight and at rest. Amongst many other other requirements all passwords
10
- in configuration files have to be encrypted
11
+ whilst in flight and at rest. Amongst many other requirements all passwords
12
+ in configuration files also have to be encrypted.
11
13
 
12
- This Gem helps achieve compliance by supporting encryption of data in a simple
13
- and consistent way
14
+ Symmetric Encryption helps achieve compliance by supporting encryption of data in a simple
15
+ and consistent way.
14
16
 
15
17
  Symmetric Encryption uses OpenSSL to encrypt and decrypt data, and can therefore
16
18
  expose all the encryption algorithms supported by OpenSSL.
@@ -19,110 +21,90 @@ expose all the encryption algorithms supported by OpenSSL.
19
21
 
20
22
  For complete documentation see: http://reidmorrison.github.io/symmetric-encryption/
21
23
 
22
- ## New features in V1.1 and V2
24
+ ## Dependencies
23
25
 
24
- * Ability to randomly generate a new initialization vector (iv) with every
25
- encryption and put the iv in the encrypted data as its header, without having
26
- to use SymmetricEncryption::Writer
26
+ Symmetric Encryption works with the following Ruby interpreters:
27
27
 
28
- * With file encryption randomly generate a new key and initialization vector (iv) with every
29
- file encryption and put the key and iv in the encrypted data as its header which
30
- is encrypted using the global key and iv
31
-
32
- * Support for compression via SymmetricEncryption.encrypt, attr_encrypted and Mongoid
33
- fields
34
-
35
- * SymmetricEncryption.encrypt has two additional optional parameters:
36
- ```
37
- random_iv [true|false]
38
- Whether the encypted value should use a random IV every time the
39
- field is encrypted.
40
- It is recommended to set this to true where feasible. If the encrypted
41
- value could be used as part of a SQL where clause, or as part
42
- of any lookup, then it must be false.
43
- Setting random_iv to true will result in a different encrypted output for
44
- the same input string.
45
- Note: Only set to true if the field will never be used as part of
46
- the where clause in an SQL query.
47
- Note: When random_iv is true it will add a 8 byte header, plus the bytes
48
- to store the random IV in every returned encrypted string, prior to the
49
- encoding if any.
50
- Default: false
51
- Highly Recommended where feasible: true
52
-
53
- compress [true|false]
54
- Whether to compress str before encryption
55
- Should only be used for large strings since compression overhead and
56
- the overhead of adding the 'magic' header may exceed any benefits of
57
- compression
58
- Note: Adds a 6 byte header prior to encoding, only if :random_iv is false
59
- Default: false
60
- ```
61
-
62
- ## Upgrading from earlier versions to SymmetricEncryption V3
28
+ * Ruby 1.9.3, 2.0, 2.1, 2.2, or greater
29
+ * JRuby 1.7, 9.0.0, or greater
30
+ * Rubinius 2.5, or greater
31
+
32
+ ## Upgrading to SymmetricEncryption V3
63
33
 
64
34
  In version 3 of SymmetricEncryption, the following changes have been made that
65
35
  may have backward compatibility issues:
66
36
 
67
- * SymmetricEncryption.decrypt no longer rotates through all the decryption keys
37
+ * `SymmetricEncryption.decrypt` no longer rotates through all the decryption keys
68
38
  when previous ciphers fail to decrypt the encrypted string.
69
39
  In a very small, yet significant number of cases it was possible to decrypt data
70
40
  using the incorrect key. Clearly the data returned was garbage, but it still
71
41
  returned a string of data instead of throwing an exception.
72
- See SymmetricEncryption.select_cipher to supply your own custom logic to
42
+ See `SymmetricEncryption.select_cipher` to supply your own custom logic to
73
43
  determine the correct cipher to use when the encrypted string does not have a
74
44
  header and multiple ciphers are defined.
75
45
 
76
- * Configuration file format prior to V1 is no longer supported
46
+ * Configuration file format prior to V1 is no longer supported.
77
47
 
78
48
  * New configuration option has been added to support setting encryption keys
79
- from environment variables
49
+ from environment variables.
80
50
 
81
- * Cipher.parse_magic_header! now returns a Struct instead of an Array
51
+ * `Cipher.parse_magic_header!` now returns a Struct instead of an Array.
82
52
 
83
- * New config options :encrypted_key and :encrypted_iv to support setting
84
- the encryption key in environment variables
53
+ * New config options `:encrypted_key` and `:encrypted_iv` to support setting
54
+ the encryption key in environment variables, or from other sources such as ldap
55
+ or a central directory service.
56
+
57
+ ## New features in V1.1 and V2
85
58
 
86
- Meta
87
- ----
59
+ * Ability to randomly generate a new initialization vector (iv) with every
60
+ encryption and put the iv in the encrypted data as its header, without having
61
+ to use `SymmetricEncryption::Writer`.
88
62
 
89
- * Code: `git clone git://github.com/reidmorrison/symmetric-encryption.git`
90
- * Home: <https://github.com/reidmorrison/symmetric-encryption>
91
- * Issues: <http://github.com/reidmorrison/symmetric-encryption/issues>
92
- * Gems: <http://rubygems.org/gems/symmetric-encryption>
63
+ * With file encryption randomly generate a new key and initialization vector (iv) with every
64
+ file encryption and put the key and iv in the encrypted data as its header which
65
+ is encrypted using the global key and iv.
66
+
67
+ * Support for compression.
68
+
69
+ * `SymmetricEncryption.encrypt` has two additional optional parameters:
70
+ * random_iv `[true|false]`
71
+ * Whether the encypted value should use a random IV every time the
72
+ field is encrypted.
73
+ * It is recommended to set this to true where feasible. If the encrypted
74
+ value could be used as part of a SQL where clause, or as part
75
+ of any lookup, then it must be false.
76
+ * Setting random_iv to true will result in a different encrypted output for
77
+ the same input string.
78
+ * Note: Only set to true if the field will never be used as part of
79
+ the where clause in an SQL query.
80
+ * Note: When random_iv is true it will add a 8 byte header, plus the bytes
81
+ to store the random IV in every returned encrypted string, prior to the
82
+ encoding if any.
83
+ * Note: Adds a 6 byte header prior to encoding, if not already configured
84
+ to add the header to all encrypted values.
85
+ * Default: false
86
+ * Highly Recommended where feasible: true
87
+
88
+ * compress [true|false]
89
+ * Whether to compress prior to encryption.
90
+ * Should only be used for large strings since compression overhead and
91
+ the overhead of adding the 'magic' header may exceed any benefits of
92
+ compression.
93
+ * Default: false
94
+
95
+ ## Versioning
93
96
 
94
97
  This project uses [Semantic Versioning](http://semver.org/).
95
98
 
96
- Author
97
- ------
99
+ ## Author
98
100
 
99
101
  [Reid Morrison](https://github.com/reidmorrison)
100
102
 
101
- Contributors
102
- ------------
103
-
104
- * [M. Scott Ford](https://github.com/mscottford)
105
- * [Adam St. John](https://github.com/astjohn)
106
-
107
- License
108
- -------
109
-
110
- Copyright 2012, 2013, 2014 Reid Morrison
111
-
112
- Licensed under the Apache License, Version 2.0 (the "License");
113
- you may not use this file except in compliance with the License.
114
- You may obtain a copy of the License at
115
-
116
- http://www.apache.org/licenses/LICENSE-2.0
103
+ ## Contributors
117
104
 
118
- Unless required by applicable law or agreed to in writing, software
119
- distributed under the License is distributed on an "AS IS" BASIS,
120
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121
- See the License for the specific language governing permissions and
122
- limitations under the License.
105
+ [Contributors](https://github.com/reidmorrison/symmetric-encryption/graphs/contributors)
123
106
 
124
- Disclaimer
125
- ----------
107
+ ## Disclaimer
126
108
 
127
109
  Although this library has assisted in meeting PCI Compliance and has passed
128
110
  previous PCI audits, it in no way guarantees that PCI Compliance will be
data/Rakefile CHANGED
@@ -1,21 +1,21 @@
1
1
  require 'rake/clean'
2
2
  require 'rake/testtask'
3
3
 
4
- $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
5
5
  require 'symmetric_encryption/version'
6
6
 
7
7
  task :gem do
8
- system "gem build symmetric-encryption.gemspec"
8
+ system 'gem build symmetric-encryption.gemspec'
9
9
  end
10
10
 
11
11
  task :publish => :gem do
12
12
  system "git tag -a v#{SymmetricEncryption::VERSION} -m 'Tagging #{SymmetricEncryption::VERSION}'"
13
- system "git push --tags"
13
+ system 'git push --tags'
14
14
  system "gem push symmetric-encryption-#{SymmetricEncryption::VERSION}.gem"
15
15
  system "rm symmetric-encryption-#{SymmetricEncryption::VERSION}.gem"
16
16
  end
17
17
 
18
- desc "Run Test Suite"
18
+ desc 'Run Test Suite'
19
19
  task :test do
20
20
  Rake::TestTask.new(:functional) do |t|
21
21
  t.test_files = FileList['test/*_test.rb']
@@ -1,12 +1,12 @@
1
1
  module SymmetricEncryption
2
2
  module Generators
3
3
  class ConfigGenerator < Rails::Generators::Base
4
- desc "Creates a SymmetricEncryption configuration file at config/symmetric-encryption.yml"
4
+ desc 'Creates a SymmetricEncryption configuration file at config/symmetric-encryption.yml'
5
5
 
6
6
  argument :key_path, type: :string, optional: false
7
7
 
8
8
  def self.source_root
9
- @_symmetric_encryption_source_root ||= File.expand_path("../templates", __FILE__)
9
+ @_symmetric_encryption_source_root ||= File.expand_path('../templates', __FILE__)
10
10
  end
11
11
 
12
12
  def app_name
@@ -14,7 +14,7 @@ module SymmetricEncryption
14
14
  end
15
15
 
16
16
  def create_config_file
17
- template 'symmetric-encryption.yml', File.join('config', "symmetric-encryption.yml")
17
+ template 'symmetric-encryption.yml', File.join('config', 'symmetric-encryption.yml')
18
18
  end
19
19
 
20
20
  end
@@ -1,10 +1,10 @@
1
1
  module SymmetricEncryption
2
2
  module Generators
3
3
  class HerokuConfigGenerator < Rails::Generators::Base
4
- desc "Creates a SymmetricEncryption configuration file at config/symmetric-encryption.yml for use in heroku"
4
+ desc 'Creates a SymmetricEncryption configuration file at config/symmetric-encryption.yml for use in heroku'
5
5
 
6
6
  def self.source_root
7
- @_symmetric_encryption_source_root ||= File.expand_path("../templates", __FILE__)
7
+ @_symmetric_encryption_source_root ||= File.expand_path('../templates', __FILE__)
8
8
  end
9
9
 
10
10
  def app_name
@@ -12,7 +12,7 @@ module SymmetricEncryption
12
12
  end
13
13
 
14
14
  def create_config_file
15
- template 'symmetric-encryption.yml', File.join('config', "symmetric-encryption.yml")
15
+ template 'symmetric-encryption.yml', File.join('config', 'symmetric-encryption.yml')
16
16
  end
17
17
 
18
18
  end
@@ -1,12 +1,12 @@
1
1
  module SymmetricEncryption
2
2
  module Generators
3
3
  class NewKeysGenerator < Rails::Generators::Base
4
- desc "Generate new Symmetric key and initialization vector based on values in config/symmetric-encryption.yml"
4
+ desc 'Generate new Symmetric key and initialization vector based on values in config/symmetric-encryption.yml'
5
5
 
6
6
  argument :environment, type: :string, optional: false
7
7
 
8
8
  def create_config_file
9
- SymmetricEncryption.generate_symmetric_key_files(File.join('config', "symmetric-encryption.yml"), environment)
9
+ SymmetricEncryption.generate_symmetric_key_files(File.join('config', 'symmetric-encryption.yml'), environment)
10
10
  end
11
11
 
12
12
  end
@@ -8,15 +8,21 @@ require 'symmetric_encryption/cipher'
8
8
  require 'symmetric_encryption/symmetric_encryption'
9
9
  require 'symmetric_encryption/exception'
10
10
 
11
+ #@formatter:off
11
12
  module SymmetricEncryption
13
+ autoload :Coerce, 'symmetric_encryption/coerce'
14
+ autoload :Config, 'symmetric_encryption/config'
12
15
  autoload :Reader, 'symmetric_encryption/reader'
13
16
  autoload :Writer, 'symmetric_encryption/writer'
14
17
  autoload :Generator, 'symmetric_encryption/generator'
15
18
  end
19
+ #@formatter:on
16
20
 
17
21
  # Add support for other libraries only if they have already been loaded
18
22
  require 'symmetric_encryption/railtie' if defined?(Rails)
19
- require 'symmetric_encryption/extensions/active_record/base' if defined?(ActiveRecord::Base)
23
+ if defined?(ActiveRecord::Base) && !defined?(AttrEncrypted::Version)
24
+ require 'symmetric_encryption/extensions/active_record/base'
25
+ end
20
26
  require 'symmetric_encryption/railties/symmetric_encryption_validator' if defined?(ActiveModel)
21
27
  require 'symmetric_encryption/extensions/mongoid/encrypted' if defined?(Mongoid)
22
28
  require 'symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key' if defined?(MongoMapper)
@@ -18,12 +18,18 @@ module SymmetricEncryption
18
18
 
19
19
  # Defines the Header Structure returned when parsing the header
20
20
  HeaderStruct = Struct.new(
21
- :compressed, # [true|false] Whether the data is compressed, if supplied in the header
22
- :iv, # [String] IV used to encrypt the data, if supplied in the header
23
- :key, # [String] Key used to encrypt the data, if supplied in the header
24
- :cipher_name, # [String] Name of the cipher used, if supplied in the header
25
- :version, # [Integer] Version of the cipher used, if supplied in the header
26
- :decryption_cipher # [SymmetricEncryption::Cipher] Cipher matching the header, or SymmetricEncryption.cipher(default_version)
21
+ # [true|false] Whether the data is compressed, if supplied in the header
22
+ :compressed,
23
+ # [String] IV used to encrypt the data, if supplied in the header
24
+ :iv,
25
+ # [String] Key used to encrypt the data, if supplied in the header
26
+ :key,
27
+ # [String] Name of the cipher used, if supplied in the header
28
+ :cipher_name,
29
+ # [Integer] Version of the cipher used, if supplied in the header
30
+ :version,
31
+ # [SymmetricEncryption::Cipher] Cipher matching the header, or SymmetricEncryption.cipher(default_version)
32
+ :decryption_cipher
27
33
  )
28
34
 
29
35
  # Generate a new Symmetric Key pair
@@ -31,6 +37,10 @@ module SymmetricEncryption
31
37
  # Returns a hash containing a new random symmetric_key pair
32
38
  # consisting of a :key and :iv.
33
39
  # The cipher_name is also included for compatibility with the Cipher initializer
40
+ #
41
+ # Notes:
42
+ # * The key _must_ be properly secured
43
+ # * The iv can be stored in the clear and it is not necessary to encrypt it
34
44
  def self.random_key_pair(cipher_name = 'aes-256-cbc')
35
45
  openssl_cipher = ::OpenSSL::Cipher.new(cipher_name)
36
46
  openssl_cipher.encrypt
@@ -42,15 +52,77 @@ module SymmetricEncryption
42
52
  }
43
53
  end
44
54
 
55
+ # Generate new randomized keys and generate key and iv files if supplied
56
+ # Overwrites key files for the current environment
57
+ # See: #initialize for parameters
58
+ def self.generate_random_keys(params)
59
+ environment = params[:environment]
60
+ private_rsa_key = config[:private_rsa_key]
61
+ rsa = OpenSSL::PKey::RSA.new(private_rsa_key) if private_rsa_key
62
+ key_pair = SymmetricEncryption::Cipher.random_key_pair(params[:cipher_name] || 'aes-256-cbc')
63
+ key = key_pair[:key]
64
+ iv = key_pair[:iv]
65
+
66
+ puts 'Generated new Symmetric Key for encryption'
67
+ if params.has_key?(:key)
68
+ puts 'Put this value in your configuration file for :key'
69
+ p key
70
+ elsif file_name = config.delete(:key_filename)
71
+ write_to_file(file_name, key, rsa)
72
+ puts("Please copy #{file_name} to the other servers in #{environment}.")
73
+ elsif params.has_key?(:encrypted_key)
74
+ encrypted_key = encrypt_key(key, rsa)
75
+ puts 'If running in Heroku, add the environment specific key:'
76
+ puts "heroku config:add #{environment.upcase}_KEY1=#{encrypted_key}"
77
+ puts
78
+ puts 'Otherwise, set the :encrypted_key value to:'
79
+ puts encrypted_key
80
+ end
81
+
82
+ puts 'Generated new Initialization Vector for encryption'
83
+ if params.has_key?(:iv)
84
+ puts 'Put this value in your configuration file for :iv'
85
+ p iv
86
+ elsif file_name = config.delete(:iv_filename)
87
+ write_to_file(file_name, iv, rsa)
88
+ puts("Please copy #{file_name} to the other servers in #{environment}.")
89
+ elsif params.has_key?(:encrypted_iv)
90
+ encrypted_iv = encrypt_key(iv, rsa)
91
+ puts 'If running in Heroku, add the environment specific key:'
92
+ puts "heroku config:add #{environment.upcase}_KEY1=#{encrypted_iv}"
93
+ puts
94
+ puts 'Otherwise, set the :encrypted_iv value to:'
95
+ puts encrypted_iv
96
+ end
97
+ end
98
+
45
99
  # Create a Symmetric::Key for encryption and decryption purposes
46
100
  #
47
101
  # Parameters:
48
102
  # :key [String]
49
103
  # The Symmetric Key to use for encryption and decryption
104
+ # Or,
105
+ # :key_filename
106
+ # Name of file containing symmetric key encrypted using the public
107
+ # key from the private_rsa_key
108
+ # Or,
109
+ # :encrypted_key
110
+ # Symmetric key encrypted using the public key from the private_rsa_key
111
+ # and then Base64 encoded
50
112
  #
51
113
  # :iv [String]
52
114
  # Optional. The Initialization Vector to use with Symmetric Key
53
115
  # Highly Recommended as it is the input into the CBC algorithm
116
+ # Or,
117
+ # Note: The following 2 options are deprecated since it is _not_ necessary
118
+ # to encrypt the initialization vector (IV)
119
+ # :iv_filename
120
+ # Name of file containing symmetric key initialization vector
121
+ # encrypted using the public key from the private_rsa_key
122
+ # Or,
123
+ # :encrypted_iv
124
+ # Initialization vector encrypted using the public key from the private_rsa_key
125
+ # and then Base64 encoded
54
126
  #
55
127
  # :cipher_name [String]
56
128
  # Optional. Encryption Cipher to use
@@ -84,16 +156,37 @@ module SymmetricEncryption
84
156
  # Default: false
85
157
  # Recommended: true
86
158
  #
159
+ # private_rsa_key [String]
160
+ # RSA Key used to decrypt key and iv as applicable
161
+ # Mandatory if :key_filename, :encrypted_key, :iv_filename, or :encrypted_iv is supplied
87
162
  def initialize(params={})
88
163
  params = params.dup
89
- @key = params.delete(:key)
90
- @iv = params.delete(:iv)
91
164
  @cipher_name = params.delete(:cipher_name) || params.delete(:cipher) || 'aes-256-cbc'
92
165
  @version = params.delete(:version)
93
166
  @always_add_header = params.delete(:always_add_header) || false
94
167
  @encoding = (params.delete(:encoding) || :base64).to_sym
95
168
 
96
- raise(ArgumentError, "Missing mandatory parameter :key") unless @key
169
+ # To decrypt encrypted key or iv files
170
+ private_rsa_key = params.delete(:private_rsa_key)
171
+ rsa = OpenSSL::PKey::RSA.new(private_rsa_key) if private_rsa_key
172
+
173
+ if key = params.delete(:key)
174
+ @key = key
175
+ elsif file_name = params.delete(:key_filename)
176
+ @key = read_from_file(file_name, rsa)
177
+ elsif encrypted_key = params.delete(:encrypted_key)
178
+ @key = decrypt_key(encrypted_key, rsa)
179
+ end
180
+
181
+ if iv = params.delete(:iv)
182
+ @iv = iv
183
+ elsif file_name = params.delete(:iv_filename)
184
+ @iv = read_from_file(file_name, rsa)
185
+ elsif encrypted_iv = params.delete(:encrypted_iv)
186
+ @iv = decrypt_key(encrypted_iv, rsa)
187
+ end
188
+
189
+ raise(ArgumentError, 'Missing mandatory parameter :key, :key_filename, or :encrypted_key') unless @key
97
190
  raise(ArgumentError, "Invalid Encoding: #{@encoding}") unless ENCODINGS.include?(@encoding)
98
191
  raise(ArgumentError, "Cipher version has a valid range of 0 to 255. #{@version} is too high, or negative") if (@version.to_i > 255) || (@version.to_i < 0)
99
192
  raise(ArgumentError, "SymmetricEncryption::Cipher Invalid options #{params.inspect}") if params.size > 0
@@ -189,16 +282,13 @@ module SymmetricEncryption
189
282
  case encoding
190
283
  when :base64
191
284
  encoded_string = ::Base64.encode64(binary_string)
192
- # Support Ruby 1.9 encoding
193
- defined?(Encoding) ? encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING) : encoded_string
285
+ encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
194
286
  when :base64strict
195
287
  encoded_string = ::Base64.encode64(binary_string).gsub(/\n/, '')
196
- # Support Ruby 1.9 encoding
197
- defined?(Encoding) ? encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING) : encoded_string
288
+ encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
198
289
  when :base16
199
290
  encoded_string = binary_string.to_s.unpack('H*').first
200
- # Support Ruby 1.9 encoding
201
- defined?(Encoding) ? encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING) : encoded_string
291
+ encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
202
292
  else
203
293
  binary_string
204
294
  end
@@ -214,12 +304,10 @@ module SymmetricEncryption
214
304
  case encoding
215
305
  when :base64, :base64strict
216
306
  decoded_string = ::Base64.decode64(encoded_string)
217
- # Support Ruby 1.9 encoding
218
- defined?(Encoding) ? decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING) : decoded_string
307
+ decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING)
219
308
  when :base16
220
309
  decoded_string = [encoded_string].pack('H*')
221
- # Support Ruby 1.9 encoding
222
- defined?(Encoding) ? decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING) : decoded_string
310
+ decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING)
223
311
  else
224
312
  encoded_string
225
313
  end
@@ -277,17 +365,17 @@ module SymmetricEncryption
277
365
  # Cipher name it UTF8 text
278
366
 
279
367
  # Remove header and extract flags
280
- _, flags = buffer.slice!(0..MAGIC_HEADER_SIZE+1).unpack(MAGIC_HEADER_UNPACK)
281
- compressed = (flags & 0b1000_0000_0000_0000) != 0
282
- include_iv = (flags & 0b0100_0000_0000_0000) != 0
283
- include_key = (flags & 0b0010_0000_0000_0000) != 0
284
- include_cipher= (flags & 0b0001_0000_0000_0000) != 0
368
+ _, flags = buffer.slice!(0..MAGIC_HEADER_SIZE+1).unpack(MAGIC_HEADER_UNPACK)
369
+ compressed = (flags & 0b1000_0000_0000_0000) != 0
370
+ include_iv = (flags & 0b0100_0000_0000_0000) != 0
371
+ include_key = (flags & 0b0010_0000_0000_0000) != 0
372
+ include_cipher = (flags & 0b0001_0000_0000_0000) != 0
285
373
  # Version of the key to use to decrypt the key if present,
286
374
  # otherwise to decrypt the data following the header
287
- version = flags & 0b0000_0000_1111_1111
375
+ version = flags & 0b0000_0000_1111_1111
288
376
  decryption_cipher = SymmetricEncryption.cipher(version)
289
377
  raise(SymmetricEncryption::CipherError, "Cipher with version:#{version.inspect} not found in any of the configured SymmetricEncryption ciphers") unless decryption_cipher
290
- iv, key, cipher_name = nil
378
+ iv, key, cipher_name = nil
291
379
 
292
380
  if include_iv
293
381
  len = buffer.slice!(0..1).unpack('v').first
@@ -298,7 +386,7 @@ module SymmetricEncryption
298
386
  key = decryption_cipher.binary_decrypt(buffer.slice!(0..len-1), header=false)
299
387
  end
300
388
  if include_cipher
301
- len = buffer.slice!(0..1).unpack('v').first
389
+ len = buffer.slice!(0..1).unpack('v').first
302
390
  cipher_name = buffer.slice!(0..len-1)
303
391
  end
304
392
 
@@ -330,14 +418,14 @@ module SymmetricEncryption
330
418
  # Ruby V2 named parameters would be perfect here
331
419
 
332
420
  # Version number of supplied encryption key, or use the global cipher version if none was supplied
333
- flags = iv || key ? (SymmetricEncryption.cipher.version || 0) : (version || 0) # Same as 0b0000_0000_0000_0000
421
+ flags = iv || key ? (SymmetricEncryption.cipher.version || 0) : (version || 0) # Same as 0b0000_0000_0000_0000
334
422
 
335
423
  # If the data is to be compressed before being encrypted, set the
336
424
  # compressed bit in the flags word
337
- flags |= 0b1000_0000_0000_0000 if compressed
338
- flags |= 0b0100_0000_0000_0000 if iv
339
- flags |= 0b0010_0000_0000_0000 if key
340
- flags |= 0b0001_0000_0000_0000 if cipher_name
425
+ flags |= 0b1000_0000_0000_0000 if compressed
426
+ flags |= 0b0100_0000_0000_0000 if iv
427
+ flags |= 0b0010_0000_0000_0000 if key
428
+ flags |= 0b0001_0000_0000_0000 if cipher_name
341
429
  header = "#{MAGIC_HEADER}#{[flags].pack('v')}".force_encoding(SymmetricEncryption::BINARY_ENCODING)
342
430
  if iv
343
431
  header << [iv.length].pack('v')
@@ -378,18 +466,19 @@ module SymmetricEncryption
378
466
  openssl_cipher = ::OpenSSL::Cipher.new(self.cipher_name)
379
467
  openssl_cipher.encrypt
380
468
  openssl_cipher.key = @key
381
- add_header = always_add_header || random_iv || compress if add_header.nil?
382
- result = if add_header
383
- # Random iv and compress both add the magic header
384
- iv = random_iv ? openssl_cipher.random_iv : @iv
385
- openssl_cipher.iv = iv if iv
386
- # Set the binary indicator on the header if string is Binary Encoded
387
- self.class.build_header(version, compress, random_iv ? iv : nil, nil, nil) +
388
- openssl_cipher.update(compress ? Zlib::Deflate.deflate(string) : string)
389
- else
390
- openssl_cipher.iv = @iv if @iv
391
- openssl_cipher.update(string)
392
- end
469
+ add_header = always_add_header || random_iv || compress if add_header.nil?
470
+ result =
471
+ if add_header
472
+ # Random iv and compress both add the magic header
473
+ iv = random_iv ? openssl_cipher.random_iv : @iv
474
+ openssl_cipher.iv = iv if iv
475
+ # Set the binary indicator on the header if string is Binary Encoded
476
+ self.class.build_header(version, compress, random_iv ? iv : nil, nil, nil) +
477
+ openssl_cipher.update(compress ? Zlib::Deflate.deflate(string) : string)
478
+ else
479
+ openssl_cipher.iv = @iv if @iv
480
+ openssl_cipher.update(string)
481
+ end
393
482
  result << openssl_cipher.final
394
483
  end
395
484
 
@@ -429,23 +518,23 @@ module SymmetricEncryption
429
518
  return str if str.empty?
430
519
 
431
520
  if header || self.class.has_header?(str)
432
- str = str.dup
521
+ str = str.dup
433
522
  header ||= self.class.parse_header!(str)
434
523
 
435
524
  openssl_cipher = ::OpenSSL::Cipher.new(header.cipher_name || self.cipher_name)
436
525
  openssl_cipher.decrypt
437
526
  openssl_cipher.key = header.key || @key
438
- iv = header.iv || @iv
439
- openssl_cipher.iv = iv if iv
440
- result = openssl_cipher.update(str)
527
+ iv = header.iv || @iv
528
+ openssl_cipher.iv = iv if iv
529
+ result = openssl_cipher.update(str)
441
530
  result << openssl_cipher.final
442
531
  header.compressed ? Zlib::Inflate.inflate(result) : result
443
532
  else
444
533
  openssl_cipher = ::OpenSSL::Cipher.new(self.cipher_name)
445
534
  openssl_cipher.decrypt
446
535
  openssl_cipher.key = @key
447
- openssl_cipher.iv = @iv if @iv
448
- result = openssl_cipher.update(str)
536
+ openssl_cipher.iv = @iv if @iv
537
+ result = openssl_cipher.update(str)
449
538
  result << openssl_cipher.final
450
539
  end
451
540
  end
@@ -458,5 +547,46 @@ module SymmetricEncryption
458
547
  private
459
548
 
460
549
  attr_reader :key
550
+
551
+ # Read the encrypted key from file
552
+ def read_from_file(file_name, rsa)
553
+ raise(SymmetricEncryption::ConfigError, 'Missing mandatory config parameter :private_rsa_key when filename key is used') unless rsa
554
+ begin
555
+ encrypted_key = File.open(file_name, 'rb') { |f| f.read }
556
+ rsa.private_decrypt(encrypted_key)
557
+ rescue Errno::ENOENT
558
+ puts "\nSymmetric Encryption key file: '#{file_name}' not found or readable."
559
+ puts "To generate the keys for the first time run: rails generate symmetric_encryption:new_keys\n\n"
560
+ end
561
+ end
562
+
563
+ # Save symmetric key after encrypting it with the private RSA key
564
+ # Backing up existing files if present
565
+ def write_to_file(file_name, key, rsa)
566
+ raise(SymmetricEncryption::ConfigError, 'Missing mandatory config parameter :private_rsa_key when filename key is used') unless rsa
567
+ File.rename(file_name, "#{file_name}.#{Time.now.to_i}") if File.exist?(file_name)
568
+ File.open(file_name, 'wb') { |file| file.write(rsa.public_encrypt(key)) }
569
+ end
570
+
571
+ # Read the encrypted key from file
572
+ def decrypt_key(encrypted_key, rsa)
573
+ raise(SymmetricEncryption::ConfigError, 'Missing mandatory config parameter :private_rsa_key when encrypted key is supplied') unless rsa
574
+
575
+ # Decode value first using encoding specified
576
+ encrypted_key = ::Base64.decode64(encrypted_key)
577
+ if !encrypted_key || encrypted_key.empty?
578
+ puts "\nSymmetric Encryption encrypted_key not found."
579
+ puts "To generate the keys for the first time run: rails generate symmetric_encryption:new_keys\n\n"
580
+ else
581
+ rsa.private_decrypt(encrypted_key)
582
+ end
583
+ end
584
+
585
+ # Returns [String] encrypted form of supplied key
586
+ def encrypt_key(key, rsa)
587
+ raise(SymmetricEncryption::ConfigError, 'Missing mandatory config parameter :private_rsa_key when encrypted key is supplied') unless rsa
588
+ ::Base64.encode64(rsa.public_encrypt(key))
589
+ end
590
+
461
591
  end
462
592
  end