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,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)