symmetric-encryption 4.3.0 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -7
  3. data/Rakefile +9 -9
  4. data/bin/symmetric-encryption +1 -1
  5. data/lib/symmetric-encryption.rb +1 -1
  6. data/lib/symmetric_encryption/cipher.rb +17 -11
  7. data/lib/symmetric_encryption/cli.rb +72 -54
  8. data/lib/symmetric_encryption/coerce.rb +3 -3
  9. data/lib/symmetric_encryption/config.rb +28 -27
  10. data/lib/symmetric_encryption/core.rb +23 -22
  11. data/lib/symmetric_encryption/encoder.rb +26 -8
  12. data/lib/symmetric_encryption/generator.rb +7 -3
  13. data/lib/symmetric_encryption/header.rb +24 -24
  14. data/lib/symmetric_encryption/key.rb +1 -1
  15. data/lib/symmetric_encryption/keystore/aws.rb +10 -13
  16. data/lib/symmetric_encryption/keystore/environment.rb +5 -5
  17. data/lib/symmetric_encryption/keystore/file.rb +27 -9
  18. data/lib/symmetric_encryption/keystore/gcp.rb +21 -18
  19. data/lib/symmetric_encryption/keystore/heroku.rb +1 -1
  20. data/lib/symmetric_encryption/keystore/memory.rb +3 -3
  21. data/lib/symmetric_encryption/keystore.rb +23 -23
  22. data/lib/symmetric_encryption/railtie.rb +9 -8
  23. data/lib/symmetric_encryption/railties/mongoid_encrypted.rb +5 -4
  24. data/lib/symmetric_encryption/railties/symmetric_encryption_validator.rb +1 -1
  25. data/lib/symmetric_encryption/reader.rb +13 -13
  26. data/lib/symmetric_encryption/rsa_key.rb +1 -1
  27. data/lib/symmetric_encryption/symmetric_encryption.rb +24 -18
  28. data/lib/symmetric_encryption/utils/aws.rb +8 -10
  29. data/lib/symmetric_encryption/utils/files.rb +3 -3
  30. data/lib/symmetric_encryption/utils/re_encrypt_files.rb +11 -11
  31. data/lib/symmetric_encryption/version.rb +1 -1
  32. data/lib/symmetric_encryption/writer.rb +20 -13
  33. data/lib/symmetric_encryption.rb +9 -9
  34. metadata +8 -9
@@ -15,7 +15,7 @@ module SymmetricEncryption
15
15
  puts "\n\n********************************************************************************"
16
16
  puts "Add the environment key to Heroku:\n\n"
17
17
  puts " heroku config:add #{key_env_var}=#{encoder.encode(encrypted_key)}"
18
- puts '********************************************************************************'
18
+ puts "********************************************************************************"
19
19
  end
20
20
  end
21
21
  end
@@ -12,10 +12,10 @@ module SymmetricEncryption
12
12
  # Notes:
13
13
  # * For development and testing purposes only!!
14
14
  # * Never store the encrypted encryption key in the source code / config file.
15
- def self.generate_data_key(cipher_name:, app_name:, environment:, version: 0, dek: nil, **args)
15
+ def self.generate_data_key(cipher_name:, app_name:, environment:, version: 0, dek: nil, **_args)
16
16
  version >= 255 ? (version = 1) : (version += 1)
17
17
 
18
- kek = SymmetricEncryption::Key.new(cipher_name: cipher_name)
18
+ kek = SymmetricEncryption::Key.new(cipher_name: cipher_name)
19
19
  dek ||= SymmetricEncryption::Key.new(cipher_name: cipher_name)
20
20
 
21
21
  encrypted_key = new(key_encrypting_key: kek).write(dek.key)
@@ -35,7 +35,7 @@ module SymmetricEncryption
35
35
 
36
36
  # Stores the Encryption key in a string.
37
37
  # Secures the Encryption key by encrypting it with a key encryption key.
38
- def initialize(encrypted_key: nil, key_encrypting_key:)
38
+ def initialize(key_encrypting_key:, encrypted_key: nil)
39
39
  @encrypted_key = encrypted_key
40
40
  @key_encrypting_key = key_encrypting_key
41
41
  end
@@ -2,12 +2,12 @@ module SymmetricEncryption
2
2
  # Encryption keys are secured in Keystores
3
3
  module Keystore
4
4
  # @formatter:off
5
- autoload :Aws, 'symmetric_encryption/keystore/aws'
6
- autoload :Environment, 'symmetric_encryption/keystore/environment'
7
- autoload :Gcp, 'symmetric_encryption/keystore/gcp'
8
- autoload :File, 'symmetric_encryption/keystore/file'
9
- autoload :Heroku, 'symmetric_encryption/keystore/heroku'
10
- autoload :Memory, 'symmetric_encryption/keystore/memory'
5
+ autoload :Aws, "symmetric_encryption/keystore/aws"
6
+ autoload :Environment, "symmetric_encryption/keystore/environment"
7
+ autoload :Gcp, "symmetric_encryption/keystore/gcp"
8
+ autoload :File, "symmetric_encryption/keystore/file"
9
+ autoload :Heroku, "symmetric_encryption/keystore/heroku"
10
+ autoload :Memory, "symmetric_encryption/keystore/memory"
11
11
  # @formatter:on
12
12
 
13
13
  # Returns [Hash] a new keystore configuration after generating data keys for each environment.
@@ -56,7 +56,7 @@ module SymmetricEncryption
56
56
  # Notes:
57
57
  # * iv_filename is no longer supported and is removed when creating a new random cipher.
58
58
  # * `iv` does not need to be encrypted and is included in the clear.
59
- def self.rotate_keys!(full_config, environments: [], app_name:, rolling_deploy: false, keystore: nil)
59
+ def self.rotate_keys!(full_config, app_name:, environments: [], rolling_deploy: false, keystore: nil)
60
60
  full_config.each_pair do |environment, cfg|
61
61
  # Only rotate keys for specified environments. Default, all
62
62
  next if !environments.empty? && !environments.include?(environment.to_sym)
@@ -69,7 +69,7 @@ module SymmetricEncryption
69
69
  # Only generate new keys for keystore's that have a key encrypting key
70
70
  next unless config[:key_encrypting_key] || config[:private_rsa_key]
71
71
 
72
- cipher_name = config[:cipher_name] || 'aes-256-cbc'
72
+ cipher_name = config[:cipher_name] || "aes-256-cbc"
73
73
 
74
74
  keystore_class = keystore ? constantize_symbol(keystore) : keystore_for(config)
75
75
 
@@ -80,7 +80,7 @@ module SymmetricEncryption
80
80
  environment: environment
81
81
  }
82
82
  args[:key_path] = ::File.dirname(config[:key_filename]) if config.key?(:key_filename)
83
- new_data_key = keystore_class.generate_data_key(args)
83
+ new_data_key = keystore_class.generate_data_key(**args)
84
84
 
85
85
  # Add as second key so that key can be published now and only used in a later deploy.
86
86
  if rolling_deploy
@@ -95,7 +95,7 @@ module SymmetricEncryption
95
95
  # Rotates just the key encrypting keys for the current cipher version.
96
96
  # The existing data encryption key is not changed, it is secured using the
97
97
  # new key encrypting keys.
98
- def self.rotate_key_encrypting_keys!(full_config, environments: [], app_name:)
98
+ def self.rotate_key_encrypting_keys!(full_config, app_name:, environments: [])
99
99
  full_config.each_pair do |environment, cfg|
100
100
  # Only rotate keys for specified environments. Default, all
101
101
  next if !environments.empty? && !environments.include?(environment.to_sym)
@@ -105,7 +105,7 @@ module SymmetricEncryption
105
105
  # Only generate new keys for keystore's that have a key encrypting key
106
106
  next unless config[:key_encrypting_key]
107
107
 
108
- version = config.delete(:version) || 1
108
+ version = config.delete(:version) || 1
109
109
  version -= 1
110
110
 
111
111
  always_add_header = config.delete(:always_add_header)
@@ -144,9 +144,9 @@ module SymmetricEncryption
144
144
  ciphers:
145
145
  [
146
146
  {
147
- key: '1234567890ABCDEF',
148
- iv: '1234567890ABCDEF',
149
- cipher_name: 'aes-128-cbc',
147
+ key: "1234567890ABCDEF",
148
+ iv: "1234567890ABCDEF",
149
+ cipher_name: "aes-128-cbc",
150
150
  version: 1
151
151
  }
152
152
  ]
@@ -156,7 +156,7 @@ module SymmetricEncryption
156
156
  # Returns [Key] by recursively navigating the config tree.
157
157
  #
158
158
  # Supports N level deep key encrypting keys.
159
- def self.read_key(key: nil, iv:, key_encrypting_key: nil, cipher_name: 'aes-256-cbc', keystore: nil, version: 0, **args)
159
+ def self.read_key(iv:, key: nil, key_encrypting_key: nil, cipher_name: "aes-256-cbc", keystore: nil, version: 0, **args)
160
160
  if key_encrypting_key.is_a?(Hash)
161
161
  # Recurse up the chain returning the parent key_encrypting_key
162
162
  key_encrypting_key = read_key(cipher_name: cipher_name, **key_encrypting_key)
@@ -185,11 +185,11 @@ module SymmetricEncryption
185
185
  elsif config[:key_env_var]
186
186
  Keystore::Environment
187
187
  else
188
- raise(ArgumentError, 'Unknown keystore supplied in config')
188
+ raise(ArgumentError, "Unknown keystore supplied in config")
189
189
  end
190
190
  end
191
191
 
192
- def self.constantize_symbol(symbol, namespace = 'SymmetricEncryption::Keystore')
192
+ def self.constantize_symbol(symbol, namespace = "SymmetricEncryption::Keystore")
193
193
  klass = "#{namespace}::#{camelize(symbol.to_s)}"
194
194
  begin
195
195
  Object.const_get(klass)
@@ -202,8 +202,8 @@ module SymmetricEncryption
202
202
  def self.camelize(term)
203
203
  string = term.to_s
204
204
  string = string.sub(/^[a-z\d]*/, &:capitalize)
205
- string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{Regexp.last_match(1)}#{Regexp.last_match(2).capitalize}" }
206
- string.gsub!('/'.freeze, '::'.freeze)
205
+ string.gsub!(%r{(?:_|(/))([a-z\d]*)}i) { "#{Regexp.last_match(1)}#{Regexp.last_match(2).capitalize}" }
206
+ string.gsub!("/".freeze, "::".freeze)
207
207
  string
208
208
  end
209
209
 
@@ -220,12 +220,12 @@ module SymmetricEncryption
220
220
 
221
221
  # Migrate old encrypted_iv
222
222
  if (encrypted_iv = config.delete(:encrypted_iv)) && private_rsa_key
223
- encrypted_iv = RSAKey.new(private_rsa_key).decrypt(encrypted_iv)
224
- config[:iv] = ::Base64.decode64(encrypted_iv)
223
+ encrypted_iv = RSAKey.new(private_rsa_key).decrypt(encrypted_iv)
224
+ config[:iv] = ::Base64.decode64(encrypted_iv)
225
225
  end
226
226
 
227
227
  # Migrate old iv_filename
228
- if (file_name = config.delete(:iv_filename)) && private_rsa_key
228
+ if (file_name = config.delete(:iv_filename)) && private_rsa_key
229
229
  encrypted_iv = ::File.read(file_name)
230
230
  config[:iv] = RSAKey.new(private_rsa_key).decrypt(encrypted_iv)
231
231
  end
@@ -234,7 +234,7 @@ module SymmetricEncryption
234
234
  config[:key_encrypting_key] = RSAKey.new(private_rsa_key) if private_rsa_key
235
235
 
236
236
  # Migrate old encrypted_key to new binary format
237
- if (encrypted_key = config[:encrypted_key]) && private_rsa_key
237
+ if (encrypted_key = config[:encrypted_key]) && private_rsa_key
238
238
  config[:encrypted_key] = ::Base64.decode64(encrypted_key)
239
239
  end
240
240
  end
@@ -29,23 +29,24 @@ module SymmetricEncryption #:nodoc:
29
29
  config.before_configuration do
30
30
  # Check if already configured
31
31
  unless ::SymmetricEncryption.cipher?
32
- app_name = Rails::Application.subclasses.first.parent.to_s.underscore
33
- env_var = ENV['SYMMETRIC_ENCRYPTION_CONFIG']
34
- config_file =
32
+ parent_method = Module.method_defined?(:module_parent) ? "module_parent" : "parent"
33
+ app_name = Rails::Application.subclasses.first.send(parent_method).to_s.underscore
34
+ env_var = ENV["SYMMETRIC_ENCRYPTION_CONFIG"]
35
+ config_file =
35
36
  if env_var
36
37
  Pathname.new(File.expand_path(env_var))
37
38
  else
38
- Rails.root.join('config', 'symmetric-encryption.yml')
39
+ Rails.root.join("config", "symmetric-encryption.yml")
39
40
  end
40
41
 
41
42
  if config_file.file?
42
43
  begin
43
- ::SymmetricEncryption::Config.load!(file_name: config_file, env: ENV['SYMMETRIC_ENCRYPTION_ENV'] || Rails.env)
44
- rescue ArgumentError => exc
44
+ ::SymmetricEncryption::Config.load!(file_name: config_file, env: ENV["SYMMETRIC_ENCRYPTION_ENV"] || Rails.env)
45
+ rescue ArgumentError => e
45
46
  puts "\nSymmetric Encryption not able to read keys."
46
- puts "#{exc.class.name} #{exc.message}"
47
+ puts "#{e.class.name} #{e.message}"
47
48
  puts "To generate a new config file and key files: symmetric-encryption --generate --app-name #{app_name}\n\n"
48
- raise(exc)
49
+ raise(e)
49
50
  end
50
51
  end
51
52
 
@@ -1,4 +1,4 @@
1
- require 'mongoid'
1
+ require "mongoid"
2
2
  # Add :encrypted option for Mongoid models
3
3
  #
4
4
  # Example:
@@ -95,12 +95,13 @@ Mongoid::Fields.option :encrypted do |model, field, options|
95
95
 
96
96
  # Support overriding the name of the decrypted attribute
97
97
  decrypted_field_name = options.delete(:decrypt_as)
98
- if decrypted_field_name.nil? && encrypted_field_name.to_s.start_with?('encrypted_')
99
- decrypted_field_name = encrypted_field_name.to_s['encrypted_'.length..-1]
98
+ if decrypted_field_name.nil? && encrypted_field_name.to_s.start_with?("encrypted_")
99
+ decrypted_field_name = encrypted_field_name.to_s["encrypted_".length..-1]
100
100
  end
101
101
 
102
102
  if decrypted_field_name.nil?
103
- raise(ArgumentError, "SymmetricEncryption for Mongoid. Encryption enabled for field #{encrypted_field_name}. It must either start with 'encrypted_' or the option :decrypt_as must be supplied")
103
+ raise(ArgumentError,
104
+ "SymmetricEncryption for Mongoid. Encryption enabled for field #{encrypted_field_name}. It must either start with 'encrypted_' or the option :decrypt_as must be supplied")
104
105
  end
105
106
 
106
107
  SymmetricEncryption::Generator.generate_decrypted_accessors(model, decrypted_field_name, encrypted_field_name, options)
@@ -15,6 +15,6 @@ class SymmetricEncryptionValidator < ActiveModel::EachValidator
15
15
  def validate_each(record, attribute, value)
16
16
  return if value.blank? || SymmetricEncryption.encrypted?(value)
17
17
 
18
- record.errors.add(attribute, 'must be a value encrypted using SymmetricEncryption.encrypt')
18
+ record.errors.add(attribute, "must be a value encrypted using SymmetricEncryption.encrypt")
19
19
  end
20
20
  end
@@ -1,4 +1,4 @@
1
- require 'openssl'
1
+ require "openssl"
2
2
 
3
3
  module SymmetricEncryption
4
4
  # Read from encrypted files and other IO streams
@@ -60,7 +60,7 @@ module SymmetricEncryption
60
60
  # csv.close if csv
61
61
  # end
62
62
  def self.open(file_name_or_stream, buffer_size: 16_384, **args, &block)
63
- ios = file_name_or_stream.is_a?(String) ? ::File.open(file_name_or_stream, 'rb') : file_name_or_stream
63
+ ios = file_name_or_stream.is_a?(String) ? ::File.open(file_name_or_stream, "rb") : file_name_or_stream
64
64
 
65
65
  begin
66
66
  file = new(ios, buffer_size: buffer_size, **args)
@@ -104,7 +104,7 @@ module SymmetricEncryption
104
104
 
105
105
  # Returns [true|false] whether the file contains the encryption header
106
106
  def self.header_present?(file_name)
107
- ::File.open(file_name, 'rb') { |file| new(file).header_present? }
107
+ ::File.open(file_name, "rb") { |file| new(file).header_present? }
108
108
  end
109
109
 
110
110
  # After opening a file Returns [true|false] whether the file being
@@ -120,9 +120,9 @@ module SymmetricEncryption
120
120
  @version = version
121
121
  @header_present = false
122
122
  @closed = false
123
- @read_buffer = ''.b
123
+ @read_buffer = "".b
124
124
 
125
- raise(ArgumentError, 'Buffer size cannot be smaller than 128') unless @buffer_size >= 128
125
+ raise(ArgumentError, "Buffer size cannot be smaller than 128") unless @buffer_size >= 128
126
126
 
127
127
  read_header
128
128
  end
@@ -185,10 +185,10 @@ module SymmetricEncryption
185
185
  # At end of file, it returns nil if no more data is available, or the last
186
186
  # remaining bytes
187
187
  def read(length = nil, outbuf = nil)
188
- data = outbuf.to_s.clear
188
+ data = outbuf.nil? ? "" : outbuf.clear
189
189
  remaining_length = length
190
190
 
191
- until remaining_length == 0 || eof?
191
+ until remaining_length&.zero? || eof?
192
192
  read_block(remaining_length) if @read_buffer.empty?
193
193
 
194
194
  if remaining_length && remaining_length < @read_buffer.length
@@ -209,7 +209,7 @@ module SymmetricEncryption
209
209
  # Raises EOFError on eof
210
210
  # The stream must be opened for reading or an IOError will be raised.
211
211
  def readline(sep_string = "\n")
212
- gets(sep_string) || raise(EOFError, 'End of file reached when trying to read a line')
212
+ gets(sep_string) || raise(EOFError, "End of file reached when trying to read a line")
213
213
  end
214
214
 
215
215
  # Reads a single decrypted line from the file up to and including the optional sep_string.
@@ -226,8 +226,8 @@ module SymmetricEncryption
226
226
  read_block
227
227
  end
228
228
  index ||= -1
229
- data = @read_buffer.slice!(0..index)
230
- @pos += data.length
229
+ data = @read_buffer.slice!(0..index)
230
+ @pos += data.length
231
231
  return nil if data.empty? && eof?
232
232
 
233
233
  data
@@ -310,7 +310,7 @@ module SymmetricEncryption
310
310
  @pos = 0
311
311
 
312
312
  # Read first block and check for the header
313
- buf = @ios.read(@buffer_size, @output_buffer ||= ''.b)
313
+ buf = @ios.read(@buffer_size, @output_buffer ||= "".b)
314
314
 
315
315
  # Use cipher specified in header, or global cipher if it has no header
316
316
  iv, key, cipher_name, cipher = nil
@@ -340,7 +340,7 @@ module SymmetricEncryption
340
340
 
341
341
  # Read a block of data and append the decrypted data in the read buffer
342
342
  def read_block(length = nil)
343
- buf = @ios.read(length || @buffer_size, @output_buffer ||= ''.b)
343
+ buf = @ios.read(length || @buffer_size, @output_buffer ||= "".b)
344
344
  decrypt(buf)
345
345
  end
346
346
 
@@ -356,7 +356,7 @@ module SymmetricEncryption
356
356
  def decrypt(buf)
357
357
  return if buf.nil? || buf.empty?
358
358
 
359
- @read_buffer << @stream_cipher.update(buf, @cipher_buffer ||= ''.b)
359
+ @read_buffer << @stream_cipher.update(buf, @cipher_buffer ||= "".b)
360
360
  @read_buffer << @stream_cipher.final if @ios.eof?
361
361
  end
362
362
  end
@@ -1,4 +1,4 @@
1
- require 'openssl'
1
+ require "openssl"
2
2
  module SymmetricEncryption
3
3
  # DEPRECATED - Internal use only
4
4
  class RSAKey
@@ -1,8 +1,8 @@
1
- require 'base64'
2
- require 'openssl'
3
- require 'zlib'
4
- require 'yaml'
5
- require 'erb'
1
+ require "base64"
2
+ require "openssl"
3
+ require "zlib"
4
+ require "yaml"
5
+ require "erb"
6
6
 
7
7
  # Encrypt using 256 Bit AES CBC symmetric key and initialization vector
8
8
  # The symmetric key is protected using the private key below and must
@@ -32,7 +32,9 @@ module SymmetricEncryption
32
32
  # cipher: 'aes-128-cbc'
33
33
  # )
34
34
  def self.cipher=(cipher)
35
- raise(ArgumentError, 'Cipher must respond to :encrypt and :decrypt') unless cipher.nil? || (cipher.respond_to?(:encrypt) && cipher.respond_to?(:decrypt))
35
+ unless cipher.nil? || (cipher.respond_to?(:encrypt) && cipher.respond_to?(:decrypt))
36
+ raise(ArgumentError, "Cipher must respond to :encrypt and :decrypt")
37
+ end
36
38
 
37
39
  @cipher = cipher
38
40
  end
@@ -45,7 +47,7 @@ module SymmetricEncryption
45
47
  unless cipher?
46
48
  raise(
47
49
  SymmetricEncryption::ConfigError,
48
- 'Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data'
50
+ "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data"
49
51
  )
50
52
  end
51
53
 
@@ -61,10 +63,12 @@ module SymmetricEncryption
61
63
 
62
64
  # Set the Secondary Symmetric Ciphers Array to be used
63
65
  def self.secondary_ciphers=(secondary_ciphers)
64
- raise(ArgumentError, 'secondary_ciphers must be a collection') unless secondary_ciphers.respond_to? :each
66
+ raise(ArgumentError, "secondary_ciphers must be a collection") unless secondary_ciphers.respond_to? :each
65
67
 
66
68
  secondary_ciphers.each do |cipher|
67
- raise(ArgumentError, 'secondary_ciphers can only consist of SymmetricEncryption::Ciphers') unless cipher.respond_to?(:encrypt) && cipher.respond_to?(:decrypt)
69
+ unless cipher.respond_to?(:encrypt) && cipher.respond_to?(:decrypt)
70
+ raise(ArgumentError, "secondary_ciphers can only consist of SymmetricEncryption::Ciphers")
71
+ end
68
72
  end
69
73
  @secondary_ciphers = secondary_ciphers
70
74
  end
@@ -121,7 +125,7 @@ module SymmetricEncryption
121
125
  # the incorrect key. Clearly the data returned is garbage, but it still
122
126
  # successfully returns a string of data
123
127
  def self.decrypt(encrypted_and_encoded_string, version: nil, type: :string)
124
- return encrypted_and_encoded_string if encrypted_and_encoded_string.nil? || (encrypted_and_encoded_string == '')
128
+ return encrypted_and_encoded_string if encrypted_and_encoded_string.nil? || (encrypted_and_encoded_string == "")
125
129
 
126
130
  str = encrypted_and_encoded_string.to_s
127
131
 
@@ -150,14 +154,16 @@ module SymmetricEncryption
150
154
  end
151
155
 
152
156
  # Try to force result to UTF-8 encoding, but if it is not valid, force it back to Binary
153
- decrypted.force_encoding(SymmetricEncryption::BINARY_ENCODING) unless decrypted.force_encoding(SymmetricEncryption::UTF8_ENCODING).valid_encoding?
157
+ unless decrypted.force_encoding(SymmetricEncryption::UTF8_ENCODING).valid_encoding?
158
+ decrypted.force_encoding(SymmetricEncryption::BINARY_ENCODING)
159
+ end
154
160
  Coerce.coerce_from_string(decrypted, type)
155
161
  end
156
162
 
157
163
  # Returns the header for the encrypted string
158
164
  # Returns [nil] if no header is present
159
165
  def self.header(encrypted_and_encoded_string)
160
- return if encrypted_and_encoded_string.nil? || (encrypted_and_encoded_string == '')
166
+ return if encrypted_and_encoded_string.nil? || (encrypted_and_encoded_string == "")
161
167
 
162
168
  # Decode before decrypting supplied string
163
169
  decoded = cipher.encoder.decode(encrypted_and_encoded_string.to_s)
@@ -212,7 +218,7 @@ module SymmetricEncryption
212
218
  # the coercible gem is available in the path.
213
219
  # Default: :string
214
220
  def self.encrypt(str, random_iv: SymmetricEncryption.randomize_iv?, compress: false, type: :string, header: cipher.always_add_header)
215
- return str if str.nil? || (str == '')
221
+ return str if str.nil? || (str == "")
216
222
 
217
223
  # Encrypt and then encode the supplied string
218
224
  cipher.encrypt(Coerce.coerce_to_string(str, type), random_iv: random_iv, compress: compress, header: header)
@@ -241,7 +247,7 @@ module SymmetricEncryption
241
247
  # * This method only works reliably when the encrypted data includes the symmetric encryption header.
242
248
  # * nil and '' are considered "encrypted" so that validations do not blow up on empty values.
243
249
  def self.encrypted?(encrypted_data)
244
- return false if encrypted_data.nil? || (encrypted_data == '')
250
+ return false if encrypted_data.nil? || (encrypted_data == "")
245
251
 
246
252
  @header ||= SymmetricEncryption.cipher.encoded_magic_header
247
253
  encrypted_data.to_s.start_with?(@header)
@@ -290,16 +296,16 @@ module SymmetricEncryption
290
296
 
291
297
  # Generate a Random password
292
298
  def self.random_password(size = 22)
293
- require 'securerandom' unless defined?(SecureRandom)
299
+ require "securerandom" unless defined?(SecureRandom)
294
300
  SecureRandom.urlsafe_base64(size)
295
301
  end
296
302
 
297
- BINARY_ENCODING = Encoding.find('binary')
298
- UTF8_ENCODING = Encoding.find('UTF-8')
303
+ BINARY_ENCODING = Encoding.find("binary")
304
+ UTF8_ENCODING = Encoding.find("UTF-8")
299
305
 
300
306
  # Defaults
301
307
  @cipher = nil
302
308
  @secondary_ciphers = []
303
309
  @select_cipher = nil
304
- @random_iv = false
310
+ @randomize_iv = false
305
311
  end
@@ -1,5 +1,5 @@
1
- require 'base64'
2
- require 'aws-sdk-kms'
1
+ require "base64"
2
+ require "aws-sdk-kms"
3
3
  module SymmetricEncryption
4
4
  module Utils
5
5
  # Wrap the AWS KMS client so that it automatically creates the Customer Master Key,
@@ -13,8 +13,8 @@ module SymmetricEncryption
13
13
 
14
14
  # TODO: Map to OpenSSL ciphers
15
15
  AWS_KEY_SPEC_MAP = {
16
- 'aes-256-cbc' => 'AES_256',
17
- 'aes-128-cbc' => 'AES_128'
16
+ "aes-256-cbc" => "AES_256",
17
+ "aes-128-cbc" => "AES_128"
18
18
  }.freeze
19
19
 
20
20
  # TODO: Move to Keystore::Aws
@@ -98,12 +98,10 @@ module SymmetricEncryption
98
98
 
99
99
  private
100
100
 
101
- attr_reader :client
102
-
103
101
  def whoami
104
102
  @whoami ||= `whoami`.strip
105
103
  rescue StandardError
106
- @whoami = 'unknown'
104
+ @whoami = "unknown"
107
105
  end
108
106
 
109
107
  # Creates a new Customer Master Key for Symmetric Encryption use.
@@ -111,10 +109,10 @@ module SymmetricEncryption
111
109
  # TODO: Add error handling and retry
112
110
 
113
111
  resp = client.create_key(
114
- description: 'Symmetric Encryption for Ruby Customer Masker Key',
112
+ description: "Symmetric Encryption for Ruby Customer Masker Key",
115
113
  tags: [
116
- {tag_key: 'CreatedAt', tag_value: Time.now.to_s},
117
- {tag_key: 'CreatedBy', tag_value: whoami}
114
+ {tag_key: "CreatedAt", tag_value: Time.now.to_s},
115
+ {tag_key: "CreatedBy", tag_value: whoami}
118
116
  ]
119
117
  )
120
118
  resp.key_metadata.key_id
@@ -6,7 +6,7 @@ module SymmetricEncryption
6
6
  attr_reader :file_name
7
7
 
8
8
  def read_file_and_decode(file_name)
9
- raise(SymmetricEncryption::ConfigError, 'file_name is mandatory for each key_file entry') unless file_name
9
+ raise(SymmetricEncryption::ConfigError, "file_name is mandatory for each key_file entry") unless file_name
10
10
 
11
11
  raise(SymmetricEncryption::ConfigError, "File #{file_name} could not be found") unless ::File.exist?(file_name)
12
12
 
@@ -31,12 +31,12 @@ module SymmetricEncryption
31
31
  key_path = ::File.dirname(file_name)
32
32
  ::FileUtils.mkdir_p(key_path) unless ::File.directory?(key_path)
33
33
  ::File.rename(file_name, "#{file_name}.#{Time.now.to_i}") if ::File.exist?(file_name)
34
- ::File.open(file_name, 'wb', 0600) { |file| file.write(data) }
34
+ ::File.open(file_name, "wb", 0o600) { |file| file.write(data) }
35
35
  end
36
36
 
37
37
  # Read from the file, raising an exception if it is not found
38
38
  def read_from_file(file_name)
39
- ::File.open(file_name, 'rb', &:read)
39
+ ::File.open(file_name, "rb", &:read)
40
40
  rescue Errno::ENOENT
41
41
  raise(SymmetricEncryption::ConfigError, "Symmetric Encryption key file: '#{file_name}' not found or readable")
42
42
  end
@@ -55,26 +55,26 @@ module SymmetricEncryption
55
55
  lines = File.read(file_name)
56
56
  hits, output_lines = re_encrypt_lines(lines)
57
57
 
58
- File.open(file_name, 'wb') { |file| file.write(output_lines) } if hits.positive?
58
+ File.open(file_name, "wb") { |file| file.write(output_lines) } if hits.positive?
59
59
  hits
60
60
  end
61
61
 
62
62
  # Replaces instances of encrypted data within lines of text with re-encrypted values
63
63
  def re_encrypt_lines(lines)
64
64
  hits = 0
65
- output_lines = ''
65
+ output_lines = ""
66
66
  r = regexp
67
67
  lines.each_line do |line|
68
68
  line.force_encoding(SymmetricEncryption::UTF8_ENCODING)
69
69
  output_lines <<
70
70
  if line.valid_encoding? && (result = line.match(r))
71
- encrypted = result[0]
72
- new_value = re_encrypt(encrypted)
73
- if new_value != encrypted
71
+ encrypted = result[0]
72
+ new_value = re_encrypt(encrypted)
73
+ if new_value == encrypted
74
+ line
75
+ else
74
76
  hits += 1
75
77
  line.gsub(encrypted, new_value)
76
- else
77
- line
78
78
  end
79
79
  else
80
80
  line
@@ -117,8 +117,8 @@ module SymmetricEncryption
117
117
  begin
118
118
  count = re_encrypt_contents(file_name)
119
119
  puts "Re-encrypted #{count} encrypted value(s) in: #{file_name}" if count.positive?
120
- rescue StandardError => exc
121
- puts "Failed re-encrypting the file contents of: #{file_name}. #{exc.class.name}: #{exc.message}"
120
+ rescue StandardError => e
121
+ puts "Failed re-encrypting the file contents of: #{file_name}. #{e.class.name}: #{e.message}"
122
122
  end
123
123
  end
124
124
  end
@@ -127,13 +127,13 @@ module SymmetricEncryption
127
127
  private
128
128
 
129
129
  def regexp
130
- @regexp ||= /#{SymmetricEncryption.cipher.encoded_magic_header}([A-Za-z0-9+\/]+[=\\n]*)/
130
+ @regexp ||= %r{#{SymmetricEncryption.cipher.encoded_magic_header}([A-Za-z0-9+/]+[=\\n]*)}
131
131
  end
132
132
 
133
133
  # Returns [Integer] encrypted file key version.
134
134
  # Returns [nil] if the file is not encrypted or does not have a header.
135
135
  def encrypted_file_version(file_name)
136
- ::File.open(file_name, 'rb') do |file|
136
+ ::File.open(file_name, "rb") do |file|
137
137
  reader = SymmetricEncryption::Reader.new(file)
138
138
  reader.version if reader.header_present?
139
139
  end
@@ -1,3 +1,3 @@
1
1
  module SymmetricEncryption
2
- VERSION = '4.3.0'.freeze
2
+ VERSION = "4.4.0".freeze
3
3
  end
@@ -1,4 +1,4 @@
1
- require 'openssl'
1
+ require "openssl"
2
2
 
3
3
  module SymmetricEncryption
4
4
  # Write to encrypted files and other IO streams.
@@ -49,10 +49,10 @@ module SymmetricEncryption
49
49
  # end
50
50
  def self.open(file_name_or_stream, compress: nil, **args)
51
51
  if file_name_or_stream.is_a?(String)
52
- file_name_or_stream = ::File.open(file_name_or_stream, 'wb')
52
+ file_name_or_stream = ::File.open(file_name_or_stream, "wb")
53
53
  compress = !(/\.(zip|gz|gzip|xls.|)\z/i === file_name_or_stream) if compress.nil?
54
- else
55
- compress = true if compress.nil?
54
+ elsif compress.nil?
55
+ compress = true
56
56
  end
57
57
 
58
58
  begin
@@ -97,15 +97,22 @@ module SymmetricEncryption
97
97
  def initialize(ios, version: nil, cipher_name: nil, header: true, random_key: true, random_iv: true, compress: false)
98
98
  # Compress is only used at this point for setting the flag in the header
99
99
  @ios = ios
100
- raise(ArgumentError, 'When :random_key is true, :random_iv must also be true') if random_key && !random_iv
101
- raise(ArgumentError, 'Cannot supply a :cipher_name unless both :random_key and :random_iv are true') if cipher_name && !random_key && !random_iv
100
+ raise(ArgumentError, "When :random_key is true, :random_iv must also be true") if random_key && !random_iv
101
+ if cipher_name && !random_key && !random_iv
102
+ raise(ArgumentError, "Cannot supply a :cipher_name unless both :random_key and :random_iv are true")
103
+ end
102
104
 
103
105
  # Cipher to encrypt the random_key, or the entire file
104
106
  cipher = SymmetricEncryption.cipher(version)
105
- raise(SymmetricEncryption::CipherError, "Cipher with version:#{version} not found in any of the configured SymmetricEncryption ciphers") unless cipher
107
+ unless cipher
108
+ raise(SymmetricEncryption::CipherError,
109
+ "Cipher with version:#{version} not found in any of the configured SymmetricEncryption ciphers")
110
+ end
106
111
 
107
112
  # Force header if compressed or using random iv, key
108
- header = Header.new(version: cipher.version, compress: compress, cipher_name: cipher_name) if (header == true) || compress || random_key || random_iv
113
+ if (header == true) || compress || random_key || random_iv
114
+ header = Header.new(version: cipher.version, compress: compress, cipher_name: cipher_name)
115
+ end
109
116
 
110
117
  @stream_cipher = ::OpenSSL::Cipher.new(cipher_name || cipher.cipher_name)
111
118
  @stream_cipher.encrypt
@@ -158,8 +165,8 @@ module SymmetricEncryption
158
165
  def write(data)
159
166
  return unless data
160
167
 
161
- bytes = data.to_s
162
- @size += bytes.size
168
+ bytes = data.to_s
169
+ @size += bytes.size
163
170
  partial = @stream_cipher.update(bytes)
164
171
  @ios.write(partial) unless partial.empty?
165
172
  data.length
@@ -168,9 +175,9 @@ module SymmetricEncryption
168
175
  def write(data)
169
176
  return unless data
170
177
 
171
- bytes = data.to_s
172
- @size += bytes.size
173
- partial = @stream_cipher.update(bytes, @cipher_buffer ||= ''.b)
178
+ bytes = data.to_s
179
+ @size += bytes.size
180
+ partial = @stream_cipher.update(bytes, @cipher_buffer ||= "".b)
174
181
  @ios.write(partial) unless partial.empty?
175
182
  data.length
176
183
  end