symmetric-encryption 4.1.2 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) 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/active_record/attr_encrypted.rb +129 -0
  7. data/lib/symmetric_encryption/active_record/encrypted_attribute.rb +37 -0
  8. data/lib/symmetric_encryption/cipher.rb +20 -14
  9. data/lib/symmetric_encryption/cli.rb +76 -58
  10. data/lib/symmetric_encryption/coerce.rb +3 -3
  11. data/lib/symmetric_encryption/config.rb +37 -28
  12. data/lib/symmetric_encryption/core.rb +35 -0
  13. data/lib/symmetric_encryption/encoder.rb +26 -8
  14. data/lib/symmetric_encryption/generator.rb +7 -3
  15. data/lib/symmetric_encryption/header.rb +24 -24
  16. data/lib/symmetric_encryption/key.rb +1 -1
  17. data/lib/symmetric_encryption/keystore/aws.rb +14 -32
  18. data/lib/symmetric_encryption/keystore/environment.rb +5 -5
  19. data/lib/symmetric_encryption/keystore/file.rb +34 -17
  20. data/lib/symmetric_encryption/keystore/gcp.rb +90 -0
  21. data/lib/symmetric_encryption/keystore/heroku.rb +1 -1
  22. data/lib/symmetric_encryption/keystore/memory.rb +3 -3
  23. data/lib/symmetric_encryption/keystore.rb +23 -22
  24. data/lib/symmetric_encryption/railtie.rb +14 -13
  25. data/lib/symmetric_encryption/{extensions/mongoid/encrypted.rb → railties/mongoid_encrypted.rb} +5 -4
  26. data/lib/symmetric_encryption/railties/symmetric_encryption_validator.rb +1 -1
  27. data/lib/symmetric_encryption/reader.rb +13 -13
  28. data/lib/symmetric_encryption/rsa_key.rb +1 -1
  29. data/lib/symmetric_encryption/symmetric_encryption.rb +56 -36
  30. data/lib/symmetric_encryption/utils/aws.rb +8 -10
  31. data/lib/symmetric_encryption/utils/files.rb +45 -0
  32. data/lib/symmetric_encryption/utils/re_encrypt_files.rb +11 -11
  33. data/lib/symmetric_encryption/version.rb +1 -1
  34. data/lib/symmetric_encryption/writer.rb +20 -13
  35. data/lib/symmetric_encryption.rb +19 -49
  36. metadata +14 -13
  37. data/lib/symmetric_encryption/extensions/active_record/base.rb +0 -110
  38. data/lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb +0 -41
@@ -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,18 +1,13 @@
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
9
9
  # be distributed separately from the application
10
10
  module SymmetricEncryption
11
- # Defaults
12
- @@cipher = nil
13
- @@secondary_ciphers = []
14
- @@select_cipher = nil
15
-
16
11
  # List of types supported when encrypting or decrypting data
17
12
  #
18
13
  # Each type maps to the built-in Ruby types as follows:
@@ -37,9 +32,11 @@ module SymmetricEncryption
37
32
  # cipher: 'aes-128-cbc'
38
33
  # )
39
34
  def self.cipher=(cipher)
40
- 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
41
38
 
42
- @@cipher = cipher
39
+ @cipher = cipher
43
40
  end
44
41
 
45
42
  # Returns the Primary Symmetric Cipher being used
@@ -50,33 +47,46 @@ module SymmetricEncryption
50
47
  unless cipher?
51
48
  raise(
52
49
  SymmetricEncryption::ConfigError,
53
- '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"
54
51
  )
55
52
  end
56
53
 
57
- return @@cipher if version.nil? || (@@cipher.version == version)
54
+ return @cipher if version.nil? || (@cipher.version == version)
58
55
 
59
- secondary_ciphers.find { |c| c.version == version } || (@@cipher if version.zero?)
56
+ secondary_ciphers.find { |c| c.version == version } || (@cipher if version.zero?)
60
57
  end
61
58
 
62
59
  # Returns whether a primary cipher has been set
63
60
  def self.cipher?
64
- !@@cipher.nil?
61
+ !@cipher.nil?
65
62
  end
66
63
 
67
64
  # Set the Secondary Symmetric Ciphers Array to be used
68
65
  def self.secondary_ciphers=(secondary_ciphers)
69
- 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
70
67
 
71
68
  secondary_ciphers.each do |cipher|
72
- 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
73
72
  end
74
- @@secondary_ciphers = secondary_ciphers
73
+ @secondary_ciphers = secondary_ciphers
75
74
  end
76
75
 
77
76
  # Returns the Primary Symmetric Cipher being used
78
77
  def self.secondary_ciphers
79
- @@secondary_ciphers
78
+ @secondary_ciphers
79
+ end
80
+
81
+ # Whether to randomize the iv by default.
82
+ # true: Generate a new random IV by default. [HIGHLY RECOMMENDED]
83
+ # false: Do not generate a new random IV by default.
84
+ def self.randomize_iv?
85
+ @randomize_iv
86
+ end
87
+
88
+ def self.randomize_iv=(randomize_iv)
89
+ @randomize_iv = randomize_iv
80
90
  end
81
91
 
82
92
  # Decrypt supplied string.
@@ -115,7 +125,7 @@ module SymmetricEncryption
115
125
  # the incorrect key. Clearly the data returned is garbage, but it still
116
126
  # successfully returns a string of data
117
127
  def self.decrypt(encrypted_and_encoded_string, version: nil, type: :string)
118
- 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 == "")
119
129
 
120
130
  str = encrypted_and_encoded_string.to_s
121
131
 
@@ -133,9 +143,9 @@ module SymmetricEncryption
133
143
  if version
134
144
  # Supplied version takes preference
135
145
  cipher(version)
136
- elsif @@select_cipher
146
+ elsif @select_cipher
137
147
  # Use cipher_selector if present to decide which cipher to use
138
- @@select_cipher.call(str, decoded)
148
+ @select_cipher.call(str, decoded)
139
149
  else
140
150
  # Global cipher
141
151
  cipher
@@ -144,14 +154,16 @@ module SymmetricEncryption
144
154
  end
145
155
 
146
156
  # Try to force result to UTF-8 encoding, but if it is not valid, force it back to Binary
147
- 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
148
160
  Coerce.coerce_from_string(decrypted, type)
149
161
  end
150
162
 
151
163
  # Returns the header for the encrypted string
152
164
  # Returns [nil] if no header is present
153
165
  def self.header(encrypted_and_encoded_string)
154
- return if encrypted_and_encoded_string.nil? || (encrypted_and_encoded_string == '')
166
+ return if encrypted_and_encoded_string.nil? || (encrypted_and_encoded_string == "")
155
167
 
156
168
  # Decode before decrypting supplied string
157
169
  decoded = cipher.encoder.decode(encrypted_and_encoded_string.to_s)
@@ -172,10 +184,12 @@ module SymmetricEncryption
172
184
  # to convert it to a string
173
185
  #
174
186
  # random_iv [true|false]
175
- # Whether the encypted value should use a random IV every time the
176
- # field is encrypted.
177
- # It is recommended to set this to true where feasible. If the encrypted
178
- # value could be used as part of a SQL where clause, or as part
187
+ # Mandatory unless `SymmetricEncryption.randomize_iv = true` has been called.
188
+ #
189
+ # Whether the encrypted value should use a random IV every time the field is encrypted.
190
+ # It is recommended to set this to true where possible.
191
+ #
192
+ # If the encrypted value could be used as part of a SQL where clause, or as part
179
193
  # of any lookup, then it must be false.
180
194
  # Setting random_iv to true will result in a different encrypted output for
181
195
  # the same input string.
@@ -203,8 +217,8 @@ module SymmetricEncryption
203
217
  # Note: If type is set to something other than :string, it's expected that
204
218
  # the coercible gem is available in the path.
205
219
  # Default: :string
206
- def self.encrypt(str, random_iv: false, compress: false, type: :string, header: cipher.always_add_header)
207
- return str if str.nil? || (str == '')
220
+ def self.encrypt(str, random_iv: SymmetricEncryption.randomize_iv?, compress: false, type: :string, header: cipher.always_add_header)
221
+ return str if str.nil? || (str == "")
208
222
 
209
223
  # Encrypt and then encode the supplied string
210
224
  cipher.encrypt(Coerce.coerce_to_string(str, type), random_iv: random_iv, compress: compress, header: header)
@@ -233,7 +247,7 @@ module SymmetricEncryption
233
247
  # * This method only works reliably when the encrypted data includes the symmetric encryption header.
234
248
  # * nil and '' are considered "encrypted" so that validations do not blow up on empty values.
235
249
  def self.encrypted?(encrypted_data)
236
- return false if encrypted_data.nil? || (encrypted_data == '')
250
+ return false if encrypted_data.nil? || (encrypted_data == "")
237
251
 
238
252
  @header ||= SymmetricEncryption.cipher.encoded_magic_header
239
253
  encrypted_data.to_s.start_with?(@header)
@@ -265,7 +279,7 @@ module SymmetricEncryption
265
279
  # encoded_str.end_with?("\n") ? SymmetricEncryption.cipher(0) : SymmetricEncryption.cipher
266
280
  # end
267
281
  def self.select_cipher(&block)
268
- @@select_cipher = block || nil
282
+ @select_cipher = block || nil
269
283
  end
270
284
 
271
285
  # Load the Encryption Configuration from a YAML file
@@ -282,10 +296,16 @@ module SymmetricEncryption
282
296
 
283
297
  # Generate a Random password
284
298
  def self.random_password(size = 22)
285
- require 'securerandom' unless defined?(SecureRandom)
299
+ require "securerandom" unless defined?(SecureRandom)
286
300
  SecureRandom.urlsafe_base64(size)
287
301
  end
288
302
 
289
- BINARY_ENCODING = Encoding.find('binary')
290
- UTF8_ENCODING = Encoding.find('UTF-8')
303
+ BINARY_ENCODING = Encoding.find("binary")
304
+ UTF8_ENCODING = Encoding.find("UTF-8")
305
+
306
+ # Defaults
307
+ @cipher = nil
308
+ @secondary_ciphers = []
309
+ @select_cipher = nil
310
+ @randomize_iv = false
291
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
@@ -0,0 +1,45 @@
1
+ module SymmetricEncryption
2
+ module Utils
3
+ module Files
4
+ private
5
+
6
+ attr_reader :file_name
7
+
8
+ def read_file_and_decode(file_name)
9
+ raise(SymmetricEncryption::ConfigError, "file_name is mandatory for each key_file entry") unless file_name
10
+
11
+ raise(SymmetricEncryption::ConfigError, "File #{file_name} could not be found") unless ::File.exist?(file_name)
12
+
13
+ # TODO: Validate that file is not globally readable.
14
+ decode64(read_from_file(file_name))
15
+ end
16
+
17
+ def write_encoded_to_file(file_name, encrypted_data_key)
18
+ write_to_file(file_name, encode64(encrypted_data_key))
19
+ end
20
+
21
+ def encode64(data)
22
+ Base64.strict_encode64(data)
23
+ end
24
+
25
+ def decode64(data)
26
+ Base64.strict_decode64(data)
27
+ end
28
+
29
+ # Write to the supplied file_name, backing up the existing file if present
30
+ def write_to_file(file_name, data)
31
+ key_path = ::File.dirname(file_name)
32
+ ::FileUtils.mkdir_p(key_path) unless ::File.directory?(key_path)
33
+ ::File.rename(file_name, "#{file_name}.#{Time.now.to_i}") if ::File.exist?(file_name)
34
+ ::File.open(file_name, "wb", 0o600) { |file| file.write(data) }
35
+ end
36
+
37
+ # Read from the file, raising an exception if it is not found
38
+ def read_from_file(file_name)
39
+ ::File.open(file_name, "rb", &:read)
40
+ rescue Errno::ENOENT
41
+ raise(SymmetricEncryption::ConfigError, "Symmetric Encryption key file: '#{file_name}' not found or readable")
42
+ end
43
+ end
44
+ end
45
+ 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.1.2'.freeze
2
+ VERSION = "4.5.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
@@ -1,61 +1,31 @@
1
- # Used for compression
2
- require 'zlib'
3
- # Used to coerce data types between string and their actual types
4
- require 'coercible'
5
-
6
- require 'symmetric_encryption/version'
7
- require 'symmetric_encryption/cipher'
8
- require 'symmetric_encryption/symmetric_encryption'
9
- require 'symmetric_encryption/exception'
10
-
11
- # @formatter:off
12
- module SymmetricEncryption
13
- autoload :Coerce, 'symmetric_encryption/coerce'
14
- autoload :Config, 'symmetric_encryption/config'
15
- autoload :Encoder, 'symmetric_encryption/encoder'
16
- autoload :Generator, 'symmetric_encryption/generator'
17
- autoload :Header, 'symmetric_encryption/header'
18
- autoload :Key, 'symmetric_encryption/key'
19
- autoload :Reader, 'symmetric_encryption/reader'
20
- autoload :RSAKey, 'symmetric_encryption/rsa_key'
21
- autoload :Writer, 'symmetric_encryption/writer'
22
- autoload :CLI, 'symmetric_encryption/cli'
23
- autoload :Keystore, 'symmetric_encryption/keystore'
24
- module Utils
25
- autoload :Aws, 'symmetric_encryption/utils/aws'
26
- autoload :ReEncryptFiles, 'symmetric_encryption/utils/re_encrypt_files'
27
- end
28
- end
29
- # @formatter:on
1
+ require "symmetric_encryption/core"
30
2
 
31
3
  # Add extensions. Gems are no longer order dependent.
32
4
  begin
33
- require 'rails'
34
- require 'symmetric_encryption/railtie'
5
+ require "rails"
6
+ require "symmetric_encryption/railtie"
35
7
  rescue LoadError
36
8
  end
37
9
 
38
10
  begin
39
- require 'active_record'
40
- require 'symmetric_encryption/extensions/active_record/base'
41
- rescue LoadError
42
- end
11
+ require "active_support"
12
+ ActiveSupport.on_load(:active_record) do
13
+ require "symmetric_encryption/active_record/attr_encrypted"
14
+ require "symmetric_encryption/railties/symmetric_encryption_validator"
43
15
 
44
- begin
45
- require 'active_model'
46
- require 'symmetric_encryption/railties/symmetric_encryption_validator'
47
- rescue LoadError
48
- end
16
+ if ActiveRecord.version >= Gem::Version.new("5.0.0")
17
+ ActiveRecord::Type.register(:encrypted, SymmetricEncryption::ActiveRecord::EncryptedAttribute)
18
+ end
49
19
 
50
- begin
51
- require 'mongoid'
52
- require 'symmetric_encryption/extensions/mongoid/encrypted'
53
- rescue LoadError
54
- end
20
+ # Remove old way of defining attributes with Rails 7 since it conflicts with the method names.
21
+ if ActiveRecord.version <= Gem::Version.new("7.0.0")
22
+ ActiveRecord::Base.include(SymmetricEncryption::ActiveRecord::AttrEncrypted)
23
+ end
24
+ end
55
25
 
56
- begin
57
- require 'mongo_mapper'
58
- warn 'MongoMapper support is deprecated. Please upgrade to Mongoid.'
59
- require 'symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key'
26
+ ActiveSupport.on_load(:mongoid) do
27
+ require "symmetric_encryption/railties/mongoid_encrypted"
28
+ require "symmetric_encryption/railties/symmetric_encryption_validator"
29
+ end
60
30
  rescue LoadError
61
31
  end