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.
- checksums.yaml +4 -4
- data/README.md +5 -7
- data/Rakefile +9 -9
- data/bin/symmetric-encryption +1 -1
- data/lib/symmetric-encryption.rb +1 -1
- data/lib/symmetric_encryption/active_record/attr_encrypted.rb +129 -0
- data/lib/symmetric_encryption/active_record/encrypted_attribute.rb +37 -0
- data/lib/symmetric_encryption/cipher.rb +20 -14
- data/lib/symmetric_encryption/cli.rb +76 -58
- data/lib/symmetric_encryption/coerce.rb +3 -3
- data/lib/symmetric_encryption/config.rb +37 -28
- data/lib/symmetric_encryption/core.rb +35 -0
- data/lib/symmetric_encryption/encoder.rb +26 -8
- data/lib/symmetric_encryption/generator.rb +7 -3
- data/lib/symmetric_encryption/header.rb +24 -24
- data/lib/symmetric_encryption/key.rb +1 -1
- data/lib/symmetric_encryption/keystore/aws.rb +14 -32
- data/lib/symmetric_encryption/keystore/environment.rb +5 -5
- data/lib/symmetric_encryption/keystore/file.rb +34 -17
- data/lib/symmetric_encryption/keystore/gcp.rb +90 -0
- data/lib/symmetric_encryption/keystore/heroku.rb +1 -1
- data/lib/symmetric_encryption/keystore/memory.rb +3 -3
- data/lib/symmetric_encryption/keystore.rb +23 -22
- data/lib/symmetric_encryption/railtie.rb +14 -13
- data/lib/symmetric_encryption/{extensions/mongoid/encrypted.rb → railties/mongoid_encrypted.rb} +5 -4
- data/lib/symmetric_encryption/railties/symmetric_encryption_validator.rb +1 -1
- data/lib/symmetric_encryption/reader.rb +13 -13
- data/lib/symmetric_encryption/rsa_key.rb +1 -1
- data/lib/symmetric_encryption/symmetric_encryption.rb +56 -36
- data/lib/symmetric_encryption/utils/aws.rb +8 -10
- data/lib/symmetric_encryption/utils/files.rb +45 -0
- data/lib/symmetric_encryption/utils/re_encrypt_files.rb +11 -11
- data/lib/symmetric_encryption/version.rb +1 -1
- data/lib/symmetric_encryption/writer.rb +20 -13
- data/lib/symmetric_encryption.rb +19 -49
- metadata +14 -13
- data/lib/symmetric_encryption/extensions/active_record/base.rb +0 -110
- data/lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb +0 -41
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
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[
|
20
|
-
@config_file_path = File.expand_path(ENV[
|
21
|
-
@app_name =
|
22
|
-
@key_path = '
|
23
|
-
@cipher_name =
|
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,
|
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
|
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.
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
121
|
+
opts.on "-m", "--migrate", "Migrate configuration file to new format." do
|
119
122
|
@migrate = true
|
120
123
|
end
|
121
124
|
|
122
|
-
opts.on
|
123
|
-
|
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
|
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
|
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
|
135
|
-
|
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
|
139
|
-
|
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
|
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
|
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
|
151
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
204
|
+
opts.on "-v", "--version", "Display Symmetric Encryption version." do
|
189
205
|
@show_version = true
|
190
206
|
end
|
191
207
|
|
192
|
-
opts.on(
|
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,
|
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
|
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
|
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,
|
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
|
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
|
313
|
+
exit(-2)
|
296
314
|
end
|
297
315
|
|
298
|
-
encrypted = HighLine.new.ask(
|
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,
|
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
|
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
|
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(
|
317
|
-
value2 = HighLine.new.ask(
|
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(
|
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,
|
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,
|
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
|
2
|
-
require
|
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 =
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
f.puts
|
43
|
-
f.puts
|
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[
|
55
|
+
env ||= defined?(Rails) ? Rails.env : ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
|
54
56
|
|
55
57
|
unless file_name
|
56
|
-
root
|
57
|
-
file_name
|
58
|
-
if (env_var = ENV[
|
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,
|
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 ||=
|
73
|
-
|
74
|
+
@config ||=
|
75
|
+
begin
|
76
|
+
raise(ConfigError, "Cannot find config file: #{file_name}") unless File.exist?(file_name)
|
74
77
|
|
75
|
-
|
76
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
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(
|
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]
|
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
|
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]
|
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
|
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(
|
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(
|
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
|
-
|
12
|
-
|
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
|
19
|
+
mod = model.const_get(:EncryptedAttributes)
|
16
20
|
else
|
17
21
|
mod = model.const_set(:EncryptedAttributes, Module.new)
|
18
22
|
model.send(:include, mod)
|