symmetric-encryption 4.1.2 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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