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