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,5 +1,5 @@
1
- require 'optparse'
2
- require 'fileutils'
1
+ require "optparse"
2
+ require "fileutils"
3
3
  module SymmetricEncryption
4
4
  class CLI
5
5
  attr_reader :key_path, :app_name, :encrypt, :config_file_path,
@@ -8,7 +8,7 @@ module SymmetricEncryption
8
8
  :environments, :cipher_name, :rolling_deploy, :rotate_keys, :rotate_kek, :prompt, :show_version,
9
9
  :cleanup_keys, :activate_key, :migrate, :regions
10
10
 
11
- KEYSTORES = %i[heroku environment file].freeze
11
+ KEYSTORES = %i[aws heroku environment file gcp].freeze
12
12
 
13
13
  def self.run!(argv)
14
14
  new(argv).run!
@@ -16,11 +16,11 @@ module SymmetricEncryption
16
16
 
17
17
  def initialize(argv)
18
18
  @version = current_version
19
- @environment = ENV['SYMMETRIC_ENCRYPTION_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
20
- @config_file_path = File.expand_path(ENV['SYMMETRIC_ENCRYPTION_CONFIG'] || 'config/symmetric-encryption.yml')
21
- @app_name = 'symmetric-encryption'
22
- @key_path = '~/.symmetric-encryption'
23
- @cipher_name = 'aes-256-cbc'
19
+ @environment = ENV["SYMMETRIC_ENCRYPTION_ENV"] || ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
20
+ @config_file_path = File.expand_path(ENV["SYMMETRIC_ENCRYPTION_CONFIG"] || "config/symmetric-encryption.yml")
21
+ @app_name = "symmetric-encryption"
22
+ @key_path = "#{ENV['HOME']}/.symmetric-encryption"
23
+ @cipher_name = "aes-256-cbc"
24
24
  @rolling_deploy = false
25
25
  @prompt = false
26
26
  @show_version = false
@@ -34,7 +34,7 @@ module SymmetricEncryption
34
34
  end
35
35
 
36
36
  def run!
37
- raise(ArgumentError, 'Cannot cleanup keys and rotate keys at the same time') if cleanup_keys && rotate_keys
37
+ raise(ArgumentError, "Cannot cleanup keys and rotate keys at the same time") if cleanup_keys && rotate_keys
38
38
 
39
39
  if show_version
40
40
  puts "Symmetric Encryption v#{VERSION}"
@@ -70,11 +70,11 @@ module SymmetricEncryption
70
70
  end
71
71
 
72
72
  def parser
73
- @parser ||= OptionParser.new do |opts|
73
+ @parser ||= OptionParser.new do |opts|
74
74
  opts.banner = <<~BANNER
75
75
  Symmetric Encryption v#{VERSION}
76
76
 
77
- For more information, see: https://rocketjob.github.io/symmetric-encryption/
77
+ For more information, see: https://encryption.rocketjob.io/
78
78
 
79
79
  Note:
80
80
  It is recommended to backup the current configuration file, or place it in version control before running
@@ -83,113 +83,129 @@ module SymmetricEncryption
83
83
  symmetric-encryption [options]
84
84
  BANNER
85
85
 
86
- opts.on '-e', '--encrypt [FILE_NAME]', 'Encrypt a file, or read from stdin if no file name is supplied.' do |file_name|
86
+ opts.on "-e", "--encrypt [FILE_NAME]", "Encrypt a file, or read from stdin if no file name is supplied." do |file_name|
87
87
  @encrypt = file_name || STDIN
88
88
  end
89
89
 
90
- opts.on '-d', '--decrypt [FILE_NAME]', 'Decrypt a file, or read from stdin if no file name is supplied.' do |file_name|
90
+ opts.on "-d", "--decrypt [FILE_NAME]", "Decrypt a file, or read from stdin if no file name is supplied." do |file_name|
91
91
  @decrypt = file_name || STDIN
92
92
  end
93
93
 
94
- opts.on '-o', '--output FILE_NAME', 'Write encrypted or decrypted file to this file, otherwise output goes to stdout.' do |file_name|
94
+ opts.on "-o", "--output FILE_NAME",
95
+ "Write encrypted or decrypted file to this file, otherwise output goes to stdout." do |file_name|
95
96
  @output_file_name = file_name
96
97
  end
97
98
 
98
- opts.on '-P', '--prompt', 'When encrypting or decrypting, prompt for a string encrypt or decrypt.' do
99
+ opts.on "-P", "--prompt", "When encrypting or decrypting, prompt for a string encrypt or decrypt." do
99
100
  @prompt = true
100
101
  end
101
102
 
102
- opts.on '-z', '--compress', 'Compress encrypted output file. [Default for encrypting files]' do
103
+ opts.on "-z", "--compress", "Compress encrypted output file. [Default for encrypting files]" do
103
104
  @compress = true
104
105
  end
105
106
 
106
- opts.on '-Z', '--no-compress', 'Does not compress the output file. [Default for encrypting strings]' do
107
+ opts.on "-Z", "--no-compress", "Does not compress the output file. [Default for encrypting strings]" do
107
108
  @compress = false
108
109
  end
109
110
 
110
- opts.on '-E', '--env ENVIRONMENT', "Environment to use in the config file. Default: SYMMETRIC_ENCRYPTION_ENV || RACK_ENV || RAILS_ENV || 'development'" do |environment|
111
+ opts.on "-E", "--env ENVIRONMENT",
112
+ "Environment to use in the config file. Default: SYMMETRIC_ENCRYPTION_ENV || RACK_ENV || RAILS_ENV || 'development'" do |environment|
111
113
  @environment = environment
112
114
  end
113
115
 
114
- opts.on '-c', '--config CONFIG_FILE_PATH', 'File name & path to the Symmetric Encryption configuration file. Default: config/symmetric-encryption.yml or Env var: `SYMMETRIC_ENCRYPTION_CONFIG`' do |path|
116
+ opts.on "-c", "--config CONFIG_FILE_PATH",
117
+ "File name & path to the Symmetric Encryption configuration file. Default: config/symmetric-encryption.yml or Env var: `SYMMETRIC_ENCRYPTION_CONFIG`" do |path|
115
118
  @config_file_path = path
116
119
  end
117
120
 
118
- opts.on '-m', '--migrate', 'Migrate configuration file to new format.' do
121
+ opts.on "-m", "--migrate", "Migrate configuration file to new format." do
119
122
  @migrate = true
120
123
  end
121
124
 
122
- opts.on '-r', '--re-encrypt [PATTERN]', 'ReEncrypt all files matching the pattern. Default: "**/*.{yml,rb}"' do |pattern|
123
- @re_encrypt = pattern || '**/*.{yml,rb}'
125
+ opts.on "-r", "--re-encrypt [PATTERN]",
126
+ 'ReEncrypt all files matching the pattern. Default: "**/*.{yml,rb}"' do |pattern|
127
+ @re_encrypt = pattern || "**/*.{yml,rb}"
124
128
  end
125
129
 
126
- opts.on '-n', '--new-password [SIZE]', 'Generate a new random password using only characters that are URL-safe base64. Default size is 22.' do |size|
130
+ opts.on "-n", "--new-password [SIZE]",
131
+ "Generate a new random password using only characters that are URL-safe base64. Default size is 22." do |size|
127
132
  @random_password = (size || 22).to_i
128
133
  end
129
134
 
130
- opts.on '-g', '--generate', 'Generate a new configuration file and encryption keys for every environment.' do |config|
135
+ opts.on "-g", "--generate", "Generate a new configuration file and encryption keys for every environment." do |config|
131
136
  @generate = config
132
137
  end
133
138
 
134
- opts.on '-s', '--keystore heroku|environment|file|aws', 'Which keystore to use during generation or re-encryption.' do |keystore|
135
- @keystore = (keystore || 'file').downcase.to_sym
139
+ opts.on "-s", "--keystore heroku|environment|file|aws|gcp",
140
+ "Which keystore to use during generation or re-encryption." do |keystore|
141
+ @keystore = (keystore || "file").downcase.to_sym
136
142
  end
137
143
 
138
- opts.on '-B', '--regions [us-east-1,us-east-2,us-west-1,us-west-2]', 'AWS KMS Regions to encrypt data key with.' do |regions|
139
- @regions = regions.to_s.split(',').collect(&:strip) if regions
144
+ opts.on "-B", "--regions [us-east-1,us-east-2,us-west-1,us-west-2]",
145
+ "AWS KMS Regions to encrypt data key with." do |regions|
146
+ @regions = regions.to_s.split(",").collect(&:strip) if regions
140
147
  end
141
148
 
142
- opts.on '-K', '--key-path KEY_PATH', 'Output path in which to write generated key files. Default: ~/.symmetric-encryption' do |path|
149
+ opts.on "-K", "--key-path KEY_PATH",
150
+ "Output path in which to write generated key files. Default: ~/.symmetric-encryption" do |path|
143
151
  @key_path = path
144
152
  end
145
153
 
146
- opts.on '-a', '--app-name NAME', 'Application name to use when generating a new configuration. Default: symmetric-encryption' do |name|
154
+ opts.on "-a", "--app-name NAME",
155
+ "Application name to use when generating a new configuration. Default: symmetric-encryption" do |name|
147
156
  @app_name = name
148
157
  end
149
158
 
150
- opts.on '-S', '--environments ENVIRONMENTS', 'Comma separated list of environments for which to generate the config file. Default: development,test,release,production' do |environments|
151
- @environments = environments.split(',').collect(&:strip).collect(&:to_sym)
159
+ opts.on "-S", "--environments ENVIRONMENTS",
160
+ "Comma separated list of environments for which to generate the config file. Default: development,test,release,production" do |environments|
161
+ @environments = environments.split(",").collect(&:strip).collect(&:to_sym)
152
162
  end
153
163
 
154
- opts.on '-C', '--cipher-name NAME', 'Name of the cipher to use when generating a new config file, or when rotating keys. Default: aes-256-cbc' do |name|
164
+ opts.on "-C", "--cipher-name NAME",
165
+ "Name of the cipher to use when generating a new config file, or when rotating keys. Default: aes-256-cbc" do |name|
155
166
  @cipher_name = name
156
167
  end
157
168
 
158
- opts.on '-R', '--rotate-keys', 'Generates a new encryption key version, encryption key files, and updates the configuration file.' do
169
+ opts.on "-R", "--rotate-keys",
170
+ "Generates a new encryption key version, encryption key files, and updates the configuration file." do
159
171
  @rotate_keys = true
160
172
  end
161
173
 
162
- opts.on '-U', '--rotate-kek', 'Replace the existing key encrypting keys only, the data encryption key is not changed, and updates the configuration file.' do
174
+ opts.on "-U", "--rotate-kek",
175
+ "Replace the existing key encrypting keys only, the data encryption key is not changed, and updates the configuration file." do
163
176
  @rotate_kek = true
164
177
  end
165
178
 
166
- opts.on '-D', '--rolling-deploy', 'During key rotation, support a rolling deploy by placing the new key second in the list so that it is not activated yet.' do
179
+ opts.on "-D", "--rolling-deploy",
180
+ "During key rotation, support a rolling deploy by placing the new key second in the list so that it is not activated yet." do
167
181
  @rolling_deploy = true
168
182
  end
169
183
 
170
- opts.on '-A', '--activate-key', 'Activates the key by moving the key with the highest version to the top.' do
184
+ opts.on "-A", "--activate-key", "Activates the key by moving the key with the highest version to the top." do
171
185
  @activate_key = true
172
186
  end
173
187
 
174
- opts.on '-X', '--cleanup-keys', 'Removes all encryption keys, except the one with the highest version from the configuration file.' do
188
+ opts.on "-X", "--cleanup-keys",
189
+ "Removes all encryption keys, except the one with the highest version from the configuration file." do
175
190
  @cleanup_keys = true
176
191
  end
177
192
 
178
- opts.on '-V', '--key-version NUMBER', 'Encryption key version to use when encrypting or re-encrypting. Default: (Current global version).' do |number|
193
+ opts.on "-V", "--key-version NUMBER",
194
+ "Encryption key version to use when encrypting or re-encrypting. Default: (Current global version)." do |number|
179
195
  @version = number.to_i
180
196
  end
181
197
 
182
- opts.on '-L', '--ciphers', 'List available OpenSSL ciphers.' do
198
+ opts.on "-L", "--ciphers", "List available OpenSSL ciphers." do
183
199
  puts "OpenSSL v#{OpenSSL::VERSION}. Available Ciphers:"
184
200
  puts OpenSSL::Cipher.ciphers.join("\n")
185
201
  exit
186
202
  end
187
203
 
188
- opts.on '-v', '--version', 'Display Symmetric Encryption version.' do
204
+ opts.on "-v", "--version", "Display Symmetric Encryption version." do
189
205
  @show_version = true
190
206
  end
191
207
 
192
- opts.on('-h', '--help', 'Prints this help.') do
208
+ opts.on("-h", "--help", "Prints this help.") do
193
209
  puts opts
194
210
  exit
195
211
  end
@@ -212,7 +228,7 @@ module SymmetricEncryption
212
228
 
213
229
  config_file_does_not_exist!
214
230
  self.environments ||= %i[development test release production]
215
- args = {
231
+ args = {
216
232
  app_name: app_name,
217
233
  environments: environments,
218
234
  cipher_name: cipher_name
@@ -237,7 +253,8 @@ module SymmetricEncryption
237
253
  end
238
254
 
239
255
  config = Config.read_file(config_file_path)
240
- SymmetricEncryption::Keystore.rotate_keys!(config, environments: environments || [], app_name: app_name, rolling_deploy: rolling_deploy, keystore: keystore)
256
+ SymmetricEncryption::Keystore.rotate_keys!(config, environments: environments || [], app_name: app_name,
257
+ rolling_deploy: rolling_deploy, keystore: keystore)
241
258
  Config.write_file(config_file_path, config)
242
259
  puts "Existing configuration file updated with new keys: #{config_file_path}"
243
260
  end
@@ -255,7 +272,7 @@ module SymmetricEncryption
255
272
  next if environments && !environments.include?(env.to_sym)
256
273
  next unless ciphers = cfg[:ciphers]
257
274
 
258
- highest = ciphers.max_by { |i| i[:version] }
275
+ highest = ciphers.max_by { |i| i[:version] }
259
276
  ciphers.clear
260
277
  ciphers << highest
261
278
  end
@@ -270,7 +287,7 @@ module SymmetricEncryption
270
287
  next if environments && !environments.include?(env.to_sym)
271
288
  next unless ciphers = cfg[:ciphers]
272
289
 
273
- highest = ciphers.max_by { |i| i[:version] }
290
+ highest = ciphers.max_by { |i| i[:version] }
274
291
  ciphers.delete(highest)
275
292
  ciphers.unshift(highest)
276
293
  end
@@ -280,7 +297,8 @@ module SymmetricEncryption
280
297
  end
281
298
 
282
299
  def encrypt_file(input_file_name)
283
- SymmetricEncryption::Writer.encrypt(source: input_file_name, target: output_file_name || STDOUT, compress: compress, version: version)
300
+ SymmetricEncryption::Writer.encrypt(source: input_file_name, target: output_file_name || STDOUT, compress: compress,
301
+ version: version)
284
302
  end
285
303
 
286
304
  def decrypt_file(input_file_name)
@@ -289,38 +307,38 @@ module SymmetricEncryption
289
307
 
290
308
  def decrypt_string
291
309
  begin
292
- require 'highline'
310
+ require "highline"
293
311
  rescue LoadError
294
312
  puts("\nPlease install gem highline before using the command line task to decrypt an entered string.\n gem install \"highline\"\n\n")
295
- exit -2
313
+ exit(-2)
296
314
  end
297
315
 
298
- encrypted = HighLine.new.ask('Enter the value to decrypt:')
316
+ encrypted = HighLine.new.ask("Enter the value to decrypt:")
299
317
  text = SymmetricEncryption.cipher(version).decrypt(encrypted)
300
318
 
301
319
  puts("\n\nEncrypted: #{encrypted}")
302
- output_file_name ? File.open(output_file_name, 'wb') { |f| f << text } : puts("Decrypted: #{text}\n\n")
320
+ output_file_name ? File.open(output_file_name, "wb") { |f| f << text } : puts("Decrypted: #{text}\n\n")
303
321
  end
304
322
 
305
323
  def encrypt_string
306
324
  begin
307
- require 'highline'
325
+ require "highline"
308
326
  rescue LoadError
309
327
  puts("\nPlease install gem highline before using the command line task to encrypt an entered string.\n gem install \"highline\"\n\n")
310
- exit -2
328
+ exit(-2)
311
329
  end
312
330
  value1 = nil
313
331
  value2 = 0
314
332
 
315
333
  while value1 != value2
316
- value1 = HighLine.new.ask('Enter the value to encrypt:') { |q| q.echo = '*' }
317
- value2 = HighLine.new.ask('Re-enter the value to encrypt:') { |q| q.echo = '*' }
334
+ value1 = HighLine.new.ask("Enter the value to encrypt:") { |q| q.echo = "*" }
335
+ value2 = HighLine.new.ask("Re-enter the value to encrypt:") { |q| q.echo = "*" }
318
336
 
319
- puts('Values do not match, please try again') if value1 != value2
337
+ puts("Values do not match, please try again") if value1 != value2
320
338
  end
321
339
  compress = false if compress.nil?
322
340
  encrypted = SymmetricEncryption.cipher(version).encrypt(value1, compress: compress)
323
- output_file_name ? File.open(output_file_name, 'wb') { |f| f << encrypted } : puts("\n\nEncrypted: #{encrypted}\n\n")
341
+ output_file_name ? File.open(output_file_name, "wb") { |f| f << encrypted } : puts("\n\nEncrypted: #{encrypted}\n\n")
324
342
  end
325
343
 
326
344
  def gen_random_password(size)
@@ -328,7 +346,7 @@ module SymmetricEncryption
328
346
  puts("\nGenerated Password: #{p}")
329
347
  encrypted = SymmetricEncryption.encrypt(p)
330
348
  puts("Encrypted: #{encrypted}\n\n")
331
- File.open(output_file_name, 'wb') { |f| f << encrypted } if output_file_name
349
+ File.open(output_file_name, "wb") { |f| f << encrypted } if output_file_name
332
350
  end
333
351
 
334
352
  def current_version
@@ -14,7 +14,7 @@ module SymmetricEncryption
14
14
  # Coerce given value into given type
15
15
  # Does not coerce json or yaml values
16
16
  def self.coerce(value, type, from_type = nil)
17
- return value if value.nil? || (value == '')
17
+ return value if value.nil? || (value == "")
18
18
 
19
19
  from_type ||= value.class
20
20
  case type
@@ -32,7 +32,7 @@ module SymmetricEncryption
32
32
  # Note: if the type is :string, then the value is returned as is, and the
33
33
  # coercible gem is not used at all.
34
34
  def self.coerce_from_string(value, type)
35
- return value if value.nil? || (value == '')
35
+ return value if value.nil? || (value == "")
36
36
 
37
37
  case type
38
38
  when :string
@@ -50,7 +50,7 @@ module SymmetricEncryption
50
50
  # Note: if the type is :string, and value is not nil, then #to_s is called
51
51
  # on the value and the coercible gem is not used at all.
52
52
  def self.coerce_to_string(value, type)
53
- return value if value.nil? || (value == '')
53
+ return value if value.nil? || (value == "")
54
54
 
55
55
  case type
56
56
  when :string
@@ -1,5 +1,5 @@
1
- require 'erb'
2
- require 'yaml'
1
+ require "erb"
2
+ require "yaml"
3
3
  module SymmetricEncryption
4
4
  class Config
5
5
  attr_reader :file_name, :env
@@ -27,7 +27,7 @@ module SymmetricEncryption
27
27
 
28
28
  # Reads the entire configuration for all environments from the supplied file name.
29
29
  def self.read_file(file_name)
30
- config = YAML.load(ERB.new(File.new(file_name).read).result)
30
+ config = load_yaml(ERB.new(File.new(file_name).read).result)
31
31
  config = deep_symbolize_keys(config)
32
32
  config.each_pair { |_env, cfg| SymmetricEncryption::Config.send(:migrate_old_formats!, cfg) }
33
33
  config
@@ -36,12 +36,14 @@ module SymmetricEncryption
36
36
  # Write the entire configuration for all environments to the supplied file name.
37
37
  def self.write_file(file_name, config)
38
38
  config = deep_stringify_keys(config)
39
- File.open(file_name, 'w') do |f|
40
- f.puts '# This file was auto generated by symmetric-encryption.'
41
- f.puts '# Recommend using symmetric-encryption to make changes.'
42
- f.puts '# For more info, run:'
43
- f.puts '# symmetric-encryption --help'
44
- f.puts '#'
39
+
40
+ FileUtils.mkdir_p(File.dirname(file_name))
41
+ File.open(file_name, "w") do |f|
42
+ f.puts "# This file was auto generated by symmetric-encryption."
43
+ f.puts "# Recommend using symmetric-encryption to make changes."
44
+ f.puts "# For more info, run:"
45
+ f.puts "# symmetric-encryption --help"
46
+ f.puts "#"
45
47
  f.write(config.to_yaml)
46
48
  end
47
49
  end
@@ -50,15 +52,15 @@ module SymmetricEncryption
50
52
  #
51
53
  # See: `.load!` for parameters.
52
54
  def initialize(file_name: nil, env: nil)
53
- env ||= defined?(Rails) ? Rails.env : ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
55
+ env ||= defined?(Rails) ? Rails.env : ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
54
56
 
55
57
  unless file_name
56
- root = defined?(Rails) ? Rails.root : '.'
57
- file_name =
58
- if (env_var = ENV['SYMMETRIC_ENCRYPTION_CONFIG'])
58
+ root = defined?(Rails) ? Rails.root : "."
59
+ file_name =
60
+ if (env_var = ENV["SYMMETRIC_ENCRYPTION_CONFIG"])
59
61
  File.expand_path(env_var)
60
62
  else
61
- File.join(root, 'config', 'symmetric-encryption.yml')
63
+ File.join(root, "config", "symmetric-encryption.yml")
62
64
  end
63
65
  raise(ConfigError, "Cannot find config file: #{file_name}") unless File.exist?(file_name)
64
66
  end
@@ -69,20 +71,21 @@ module SymmetricEncryption
69
71
 
70
72
  # Returns [Hash] the configuration for the supplied environment.
71
73
  def config
72
- @config ||= begin
73
- raise(ConfigError, "Cannot find config file: #{file_name}") unless File.exist?(file_name)
74
+ @config ||=
75
+ begin
76
+ raise(ConfigError, "Cannot find config file: #{file_name}") unless File.exist?(file_name)
74
77
 
75
- env_config = YAML.load(ERB.new(File.new(file_name).read).result)[env]
76
- raise(ConfigError, "Cannot find environment: #{env} in config file: #{file_name}") unless env_config
78
+ env_config = self.class.load_yaml(ERB.new(File.new(file_name).read).result)[env]
79
+ raise(ConfigError, "Cannot find environment: #{env} in config file: #{file_name}") unless env_config
77
80
 
78
- env_config = self.class.send(:deep_symbolize_keys, env_config)
79
- self.class.send(:migrate_old_formats!, env_config)
80
- end
81
+ env_config = self.class.send(:deep_symbolize_keys, env_config)
82
+ self.class.send(:migrate_old_formats!, env_config)
83
+ end
81
84
  end
82
85
 
83
- # Returns [Array(SymmetricEncrytion::Cipher)] ciphers specified in the configuration file.
86
+ # Returns [Array(SymmetricEncryption::Cipher)] ciphers specified in the configuration file.
84
87
  def ciphers
85
- @ciphers ||= config[:ciphers].collect { |cipher_config| Cipher.from_config(cipher_config) }
88
+ @ciphers ||= config[:ciphers].collect { |cipher_config| Cipher.from_config(**cipher_config) }
86
89
  end
87
90
 
88
91
  # Iterate through the Hash symbolizing all keys.
@@ -127,22 +130,22 @@ module SymmetricEncryption
127
130
  def self.migrate_old_formats!(config)
128
131
  # Inline single cipher before :ciphers
129
132
  unless config.key?(:ciphers)
130
- inline_cipher = {}
133
+ inline_cipher = {}
131
134
  config.keys.each { |key| inline_cipher[key] = config.delete(key) }
132
- config[:ciphers] = [inline_cipher]
135
+ config[:ciphers] = [inline_cipher]
133
136
  end
134
137
 
135
138
  # Copy Old :private_rsa_key into each ciphers config
136
139
  # Cipher.from_config replaces it with the RSA Kek
137
140
  if config[:private_rsa_key]
138
- private_rsa_key = config.delete(:private_rsa_key)
141
+ private_rsa_key = config.delete(:private_rsa_key)
139
142
  config[:ciphers].each { |cipher| cipher[:private_rsa_key] = private_rsa_key }
140
143
  end
141
144
 
142
145
  # Old :cipher_name
143
146
  config[:ciphers].each do |cipher|
144
147
  if (old_key_name_cipher = cipher.delete(:cipher))
145
- cipher[:cipher_name] = old_key_name_cipher
148
+ cipher[:cipher_name] = old_key_name_cipher
146
149
  end
147
150
 
148
151
  # Only temporarily used during v4 Beta process
@@ -153,12 +156,18 @@ module SymmetricEncryption
153
156
  # encrypted_key: <%= ENV['VAR'] %>
154
157
  if cipher.key?(:encrypted_key) && cipher[:encrypted_key].nil?
155
158
  cipher[:key_env_var] = :placeholder
156
- puts 'WARNING: :encrypted_key resolved to nil. Please see the migrated config file for the new option :key_env_var.'
159
+ puts "WARNING: :encrypted_key resolved to nil. Please see the migrated config file for the new option :key_env_var."
157
160
  end
158
161
  end
159
162
  config
160
163
  end
161
164
 
162
165
  private_class_method :migrate_old_formats!
166
+
167
+ def self.load_yaml(src)
168
+ return YAML.safe_load(src, permitted_classes: [Symbol], aliases: true) if Psych::VERSION.to_i >= 4
169
+
170
+ YAML.load(src)
171
+ end
163
172
  end
164
173
  end
@@ -0,0 +1,35 @@
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 :EncryptedStringType, "symmetric_encryption/types/encrypted_string_type"
17
+ autoload :Generator, "symmetric_encryption/generator"
18
+ autoload :Header, "symmetric_encryption/header"
19
+ autoload :Key, "symmetric_encryption/key"
20
+ autoload :Reader, "symmetric_encryption/reader"
21
+ autoload :RSAKey, "symmetric_encryption/rsa_key"
22
+ autoload :Writer, "symmetric_encryption/writer"
23
+ autoload :CLI, "symmetric_encryption/cli"
24
+ autoload :Keystore, "symmetric_encryption/keystore"
25
+ module ActiveRecord
26
+ autoload :EncryptedAttribute, "symmetric_encryption/active_record/encrypted_attribute"
27
+ end
28
+
29
+ module Utils
30
+ autoload :Aws, "symmetric_encryption/utils/aws"
31
+ autoload :Files, "symmetric_encryption/utils/files"
32
+ autoload :ReEncryptFiles, "symmetric_encryption/utils/re_encrypt_files"
33
+ end
34
+ end
35
+ # @formatter:on
@@ -6,6 +6,8 @@ module SymmetricEncryption
6
6
  Base64.new
7
7
  when :base64strict
8
8
  Base64Strict.new
9
+ when :base64urlsafe
10
+ Base64UrlSafe.new
9
11
  when :base16
10
12
  Base16.new
11
13
  when :none
@@ -35,14 +37,14 @@ module SymmetricEncryption
35
37
 
36
38
  class Base64
37
39
  def encode(binary_string)
38
- return binary_string if binary_string.nil? || (binary_string == '')
40
+ return binary_string if binary_string.nil? || (binary_string == "")
39
41
 
40
42
  encoded_string = ::Base64.encode64(binary_string)
41
43
  encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
42
44
  end
43
45
 
44
46
  def decode(encoded_string)
45
- return encoded_string if encoded_string.nil? || (encoded_string == '')
47
+ return encoded_string if encoded_string.nil? || (encoded_string == "")
46
48
 
47
49
  decoded_string = ::Base64.decode64(encoded_string)
48
50
  decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING)
@@ -51,32 +53,48 @@ module SymmetricEncryption
51
53
 
52
54
  class Base64Strict
53
55
  def encode(binary_string)
54
- return binary_string if binary_string.nil? || (binary_string == '')
56
+ return binary_string if binary_string.nil? || (binary_string == "")
55
57
 
56
58
  encoded_string = ::Base64.strict_encode64(binary_string)
57
59
  encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
58
60
  end
59
61
 
60
62
  def decode(encoded_string)
61
- return encoded_string if encoded_string.nil? || (encoded_string == '')
63
+ return encoded_string if encoded_string.nil? || (encoded_string == "")
62
64
 
63
65
  decoded_string = ::Base64.decode64(encoded_string)
64
66
  decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING)
65
67
  end
66
68
  end
67
69
 
70
+ class Base64UrlSafe
71
+ def encode(binary_string)
72
+ return binary_string if binary_string.nil? || (binary_string == "")
73
+
74
+ encoded_string = ::Base64.urlsafe_encode64(binary_string)
75
+ encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
76
+ end
77
+
78
+ def decode(encoded_string)
79
+ return encoded_string if encoded_string.nil? || (encoded_string == "")
80
+
81
+ decoded_string = ::Base64.urlsafe_decode64(encoded_string)
82
+ decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING)
83
+ end
84
+ end
85
+
68
86
  class Base16
69
87
  def encode(binary_string)
70
- return binary_string if binary_string.nil? || (binary_string == '')
88
+ return binary_string if binary_string.nil? || (binary_string == "")
71
89
 
72
- encoded_string = binary_string.to_s.unpack('H*').first
90
+ encoded_string = binary_string.to_s.unpack("H*").first
73
91
  encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
74
92
  end
75
93
 
76
94
  def decode(encoded_string)
77
- return encoded_string if encoded_string.nil? || (encoded_string == '')
95
+ return encoded_string if encoded_string.nil? || (encoded_string == "")
78
96
 
79
- decoded_string = [encoded_string].pack('H*')
97
+ decoded_string = [encoded_string].pack("H*")
80
98
  decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING)
81
99
  end
82
100
  end
@@ -8,11 +8,15 @@ module SymmetricEncryption
8
8
  compress = options.delete(:compress) || false
9
9
  type = options.delete(:type) || :string
10
10
 
11
- raise(ArgumentError, "SymmetricEncryption Invalid options #{options.inspect} when encrypting '#{decrypted_name}'") unless options.empty?
12
- raise(ArgumentError, "Invalid type: #{type.inspect}. Valid types: #{SymmetricEncryption::COERCION_TYPES.inspect}") unless SymmetricEncryption::COERCION_TYPES.include?(type)
11
+ unless options.empty?
12
+ raise(ArgumentError, "SymmetricEncryption Invalid options #{options.inspect} when encrypting '#{decrypted_name}'")
13
+ end
14
+ unless SymmetricEncryption::COERCION_TYPES.include?(type)
15
+ raise(ArgumentError, "Invalid type: #{type.inspect}. Valid types: #{SymmetricEncryption::COERCION_TYPES.inspect}")
16
+ end
13
17
 
14
18
  if model.const_defined?(:EncryptedAttributes, _search_ancestors = false)
15
- mod = model.const_get(:EncryptedAttributes)
19
+ mod = model.const_get(:EncryptedAttributes)
16
20
  else
17
21
  mod = model.const_set(:EncryptedAttributes, Module.new)
18
22
  model.send(:include, mod)