symmetric-encryption 4.3.0 → 4.4.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/cipher.rb +17 -11
- data/lib/symmetric_encryption/cli.rb +72 -54
- data/lib/symmetric_encryption/coerce.rb +3 -3
- data/lib/symmetric_encryption/config.rb +28 -27
- data/lib/symmetric_encryption/core.rb +23 -22
- 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 +10 -13
- data/lib/symmetric_encryption/keystore/environment.rb +5 -5
- data/lib/symmetric_encryption/keystore/file.rb +27 -9
- data/lib/symmetric_encryption/keystore/gcp.rb +21 -18
- 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 -23
- data/lib/symmetric_encryption/railtie.rb +9 -8
- data/lib/symmetric_encryption/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 +24 -18
- data/lib/symmetric_encryption/utils/aws.rb +8 -10
- data/lib/symmetric_encryption/utils/files.rb +3 -3
- 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 +9 -9
- metadata +8 -9
@@ -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
|
@@ -38,12 +38,12 @@ module SymmetricEncryption
|
|
38
38
|
config = deep_stringify_keys(config)
|
39
39
|
|
40
40
|
FileUtils.mkdir_p(File.dirname(file_name))
|
41
|
-
File.open(file_name,
|
42
|
-
f.puts
|
43
|
-
f.puts
|
44
|
-
f.puts
|
45
|
-
f.puts
|
46
|
-
f.puts
|
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 "#"
|
47
47
|
f.write(config.to_yaml)
|
48
48
|
end
|
49
49
|
end
|
@@ -52,15 +52,15 @@ module SymmetricEncryption
|
|
52
52
|
#
|
53
53
|
# See: `.load!` for parameters.
|
54
54
|
def initialize(file_name: nil, env: nil)
|
55
|
-
env ||= defined?(Rails) ? Rails.env : ENV[
|
55
|
+
env ||= defined?(Rails) ? Rails.env : ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
|
56
56
|
|
57
57
|
unless file_name
|
58
|
-
root
|
59
|
-
file_name
|
60
|
-
if (env_var = ENV[
|
58
|
+
root = defined?(Rails) ? Rails.root : "."
|
59
|
+
file_name =
|
60
|
+
if (env_var = ENV["SYMMETRIC_ENCRYPTION_CONFIG"])
|
61
61
|
File.expand_path(env_var)
|
62
62
|
else
|
63
|
-
File.join(root,
|
63
|
+
File.join(root, "config", "symmetric-encryption.yml")
|
64
64
|
end
|
65
65
|
raise(ConfigError, "Cannot find config file: #{file_name}") unless File.exist?(file_name)
|
66
66
|
end
|
@@ -71,20 +71,21 @@ module SymmetricEncryption
|
|
71
71
|
|
72
72
|
# Returns [Hash] the configuration for the supplied environment.
|
73
73
|
def config
|
74
|
-
@config ||=
|
75
|
-
|
74
|
+
@config ||=
|
75
|
+
begin
|
76
|
+
raise(ConfigError, "Cannot find config file: #{file_name}") unless File.exist?(file_name)
|
76
77
|
|
77
|
-
|
78
|
-
|
78
|
+
env_config = YAML.load(ERB.new(File.new(file_name).read).result)[env]
|
79
|
+
raise(ConfigError, "Cannot find environment: #{env} in config file: #{file_name}") unless env_config
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
81
|
+
env_config = self.class.send(:deep_symbolize_keys, env_config)
|
82
|
+
self.class.send(:migrate_old_formats!, env_config)
|
83
|
+
end
|
83
84
|
end
|
84
85
|
|
85
|
-
# Returns [Array(
|
86
|
+
# Returns [Array(SymmetricEncryption::Cipher)] ciphers specified in the configuration file.
|
86
87
|
def ciphers
|
87
|
-
@ciphers ||= config[:ciphers].collect { |cipher_config| Cipher.from_config(cipher_config) }
|
88
|
+
@ciphers ||= config[:ciphers].collect { |cipher_config| Cipher.from_config(**cipher_config) }
|
88
89
|
end
|
89
90
|
|
90
91
|
# Iterate through the Hash symbolizing all keys.
|
@@ -129,22 +130,22 @@ module SymmetricEncryption
|
|
129
130
|
def self.migrate_old_formats!(config)
|
130
131
|
# Inline single cipher before :ciphers
|
131
132
|
unless config.key?(:ciphers)
|
132
|
-
inline_cipher
|
133
|
+
inline_cipher = {}
|
133
134
|
config.keys.each { |key| inline_cipher[key] = config.delete(key) }
|
134
|
-
config[:ciphers]
|
135
|
+
config[:ciphers] = [inline_cipher]
|
135
136
|
end
|
136
137
|
|
137
138
|
# Copy Old :private_rsa_key into each ciphers config
|
138
139
|
# Cipher.from_config replaces it with the RSA Kek
|
139
140
|
if config[:private_rsa_key]
|
140
|
-
private_rsa_key
|
141
|
+
private_rsa_key = config.delete(:private_rsa_key)
|
141
142
|
config[:ciphers].each { |cipher| cipher[:private_rsa_key] = private_rsa_key }
|
142
143
|
end
|
143
144
|
|
144
145
|
# Old :cipher_name
|
145
146
|
config[:ciphers].each do |cipher|
|
146
147
|
if (old_key_name_cipher = cipher.delete(:cipher))
|
147
|
-
cipher[:cipher_name]
|
148
|
+
cipher[:cipher_name] = old_key_name_cipher
|
148
149
|
end
|
149
150
|
|
150
151
|
# Only temporarily used during v4 Beta process
|
@@ -155,7 +156,7 @@ module SymmetricEncryption
|
|
155
156
|
# encrypted_key: <%= ENV['VAR'] %>
|
156
157
|
if cipher.key?(:encrypted_key) && cipher[:encrypted_key].nil?
|
157
158
|
cipher[:key_env_var] = :placeholder
|
158
|
-
puts
|
159
|
+
puts "WARNING: :encrypted_key resolved to nil. Please see the migrated config file for the new option :key_env_var."
|
159
160
|
end
|
160
161
|
end
|
161
162
|
config
|
@@ -1,34 +1,35 @@
|
|
1
1
|
# Used for compression
|
2
|
-
require
|
2
|
+
require "zlib"
|
3
3
|
# Used to coerce data types between string and their actual types
|
4
|
-
require
|
4
|
+
require "coercible"
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
6
|
+
require "symmetric_encryption/version"
|
7
|
+
require "symmetric_encryption/cipher"
|
8
|
+
require "symmetric_encryption/symmetric_encryption"
|
9
|
+
require "symmetric_encryption/exception"
|
10
10
|
|
11
11
|
# @formatter:off
|
12
12
|
module SymmetricEncryption
|
13
|
-
autoload :Coerce,
|
14
|
-
autoload :Config,
|
15
|
-
autoload :Encoder,
|
16
|
-
autoload :EncryptedStringType,
|
17
|
-
autoload :Generator,
|
18
|
-
autoload :Header,
|
19
|
-
autoload :Key,
|
20
|
-
autoload :Reader,
|
21
|
-
autoload :RSAKey,
|
22
|
-
autoload :Writer,
|
23
|
-
autoload :CLI,
|
24
|
-
autoload :Keystore,
|
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
25
|
module ActiveRecord
|
26
|
-
autoload :EncryptedAttribute,
|
26
|
+
autoload :EncryptedAttribute, "symmetric_encryption/active_record/encrypted_attribute"
|
27
27
|
end
|
28
|
+
|
28
29
|
module Utils
|
29
|
-
autoload :Aws,
|
30
|
-
autoload :Files,
|
31
|
-
autoload :ReEncryptFiles,
|
30
|
+
autoload :Aws, "symmetric_encryption/utils/aws"
|
31
|
+
autoload :Files, "symmetric_encryption/utils/files"
|
32
|
+
autoload :ReEncryptFiles, "symmetric_encryption/utils/re_encrypt_files"
|
32
33
|
end
|
33
34
|
end
|
34
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)
|
@@ -8,7 +8,7 @@ module SymmetricEncryption
|
|
8
8
|
class Header
|
9
9
|
# Encrypted data includes this header prior to encoding when
|
10
10
|
# `always_add_header` is true.
|
11
|
-
MAGIC_HEADER =
|
11
|
+
MAGIC_HEADER = "@EnC".force_encoding(SymmetricEncryption::BINARY_ENCODING)
|
12
12
|
MAGIC_HEADER_SIZE = MAGIC_HEADER.size
|
13
13
|
|
14
14
|
# [true|false] Whether to compress the data before encryption.
|
@@ -37,7 +37,7 @@ module SymmetricEncryption
|
|
37
37
|
# Returns whether the supplied buffer starts with a symmetric_encryption header
|
38
38
|
# Note: The encoding of the supplied buffer is forced to binary if not already binary
|
39
39
|
def self.present?(buffer)
|
40
|
-
return false if buffer.nil? || (buffer ==
|
40
|
+
return false if buffer.nil? || (buffer == "")
|
41
41
|
|
42
42
|
buffer.force_encoding(SymmetricEncryption::BINARY_ENCODING)
|
43
43
|
buffer.start_with?(MAGIC_HEADER)
|
@@ -122,7 +122,7 @@ module SymmetricEncryption
|
|
122
122
|
#
|
123
123
|
# Returns 0 if no header is present
|
124
124
|
def parse(buffer, offset = 0)
|
125
|
-
return 0 if buffer.nil? || (buffer ==
|
125
|
+
return 0 if buffer.nil? || (buffer == "") || (buffer.length <= MAGIC_HEADER_SIZE + 2)
|
126
126
|
|
127
127
|
# Symmetric Encryption Header
|
128
128
|
#
|
@@ -153,7 +153,7 @@ module SymmetricEncryption
|
|
153
153
|
|
154
154
|
# Remove header and extract flags
|
155
155
|
self.version = buffer.getbyte(offset)
|
156
|
-
offset
|
156
|
+
offset += 1
|
157
157
|
|
158
158
|
unless cipher
|
159
159
|
raise(
|
@@ -162,34 +162,34 @@ module SymmetricEncryption
|
|
162
162
|
)
|
163
163
|
end
|
164
164
|
|
165
|
-
flags
|
165
|
+
flags = buffer.getbyte(offset)
|
166
166
|
offset += 1
|
167
167
|
|
168
168
|
self.compress = (flags & FLAG_COMPRESSED) != 0
|
169
169
|
|
170
|
-
if (flags & FLAG_IV)
|
171
|
-
self.iv, offset = read_string(buffer, offset)
|
172
|
-
else
|
170
|
+
if (flags & FLAG_IV).zero?
|
173
171
|
self.iv = nil
|
172
|
+
else
|
173
|
+
self.iv, offset = read_string(buffer, offset)
|
174
174
|
end
|
175
175
|
|
176
|
-
if (flags & FLAG_KEY)
|
176
|
+
if (flags & FLAG_KEY).zero?
|
177
|
+
self.key = nil
|
178
|
+
else
|
177
179
|
encrypted_key, offset = read_string(buffer, offset)
|
178
180
|
self.key = cipher.binary_decrypt(encrypted_key)
|
179
|
-
else
|
180
|
-
self.key = nil
|
181
181
|
end
|
182
182
|
|
183
|
-
if (flags & FLAG_CIPHER_NAME)
|
184
|
-
self.cipher_name, offset = read_string(buffer, offset)
|
185
|
-
else
|
183
|
+
if (flags & FLAG_CIPHER_NAME).zero?
|
186
184
|
self.cipher_name = nil
|
185
|
+
else
|
186
|
+
self.cipher_name, offset = read_string(buffer, offset)
|
187
187
|
end
|
188
188
|
|
189
|
-
if (flags & FLAG_AUTH_TAG)
|
190
|
-
self.auth_tag, offset = read_string(buffer, offset)
|
191
|
-
else
|
189
|
+
if (flags & FLAG_AUTH_TAG).zero?
|
192
190
|
self.auth_tag = nil
|
191
|
+
else
|
192
|
+
self.auth_tag, offset = read_string(buffer, offset)
|
193
193
|
end
|
194
194
|
|
195
195
|
offset
|
@@ -197,7 +197,7 @@ module SymmetricEncryption
|
|
197
197
|
|
198
198
|
# Returns [String] this header as a string
|
199
199
|
def to_s
|
200
|
-
flags
|
200
|
+
flags = 0
|
201
201
|
flags |= FLAG_COMPRESSED if compressed?
|
202
202
|
flags |= FLAG_IV if iv
|
203
203
|
flags |= FLAG_KEY if key
|
@@ -207,23 +207,23 @@ module SymmetricEncryption
|
|
207
207
|
header = "#{MAGIC_HEADER}#{version.chr(SymmetricEncryption::BINARY_ENCODING)}#{flags.chr(SymmetricEncryption::BINARY_ENCODING)}"
|
208
208
|
|
209
209
|
if iv
|
210
|
-
header << [iv.length].pack(
|
210
|
+
header << [iv.length].pack("v")
|
211
211
|
header << iv
|
212
212
|
end
|
213
213
|
|
214
214
|
if key
|
215
215
|
encrypted = cipher.binary_encrypt(key, header: false)
|
216
|
-
header << [encrypted.length].pack(
|
216
|
+
header << [encrypted.length].pack("v")
|
217
217
|
header << encrypted
|
218
218
|
end
|
219
219
|
|
220
220
|
if cipher_name
|
221
|
-
header << [cipher_name.length].pack(
|
221
|
+
header << [cipher_name.length].pack("v")
|
222
222
|
header << cipher_name
|
223
223
|
end
|
224
224
|
|
225
225
|
if auth_tag
|
226
|
-
header << [auth_tag.length].pack(
|
226
|
+
header << [auth_tag.length].pack("v")
|
227
227
|
header << auth_tag
|
228
228
|
end
|
229
229
|
|
@@ -258,9 +258,9 @@ module SymmetricEncryption
|
|
258
258
|
# Exception when
|
259
259
|
# - offset exceeds length of buffer
|
260
260
|
# byteslice truncates when too long, but returns nil when start is beyond end of buffer
|
261
|
-
len
|
261
|
+
len = buffer.byteslice(offset, 2).unpack("v").first
|
262
262
|
offset += 2
|
263
|
-
out
|
263
|
+
out = buffer.byteslice(offset, len)
|
264
264
|
[out, offset + len]
|
265
265
|
end
|
266
266
|
end
|
@@ -3,7 +3,7 @@ module SymmetricEncryption
|
|
3
3
|
class Key
|
4
4
|
attr_reader :key, :iv, :cipher_name
|
5
5
|
|
6
|
-
def initialize(key: :random, iv: :random, cipher_name:
|
6
|
+
def initialize(key: :random, iv: :random, cipher_name: "aes-256-cbc")
|
7
7
|
@key = key == :random ? ::OpenSSL::Cipher.new(cipher_name).random_key : key
|
8
8
|
@iv = iv == :random ? ::OpenSSL::Cipher.new(cipher_name).random_iv : iv
|
9
9
|
@cipher_name = cipher_name
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "aws-sdk-kms"
|
2
2
|
module SymmetricEncryption
|
3
3
|
module Keystore
|
4
4
|
# Support AWS Key Management Service (KMS)
|
@@ -70,24 +70,20 @@ module SymmetricEncryption
|
|
70
70
|
# ],
|
71
71
|
# iv: 'T80pYzD0E6e/bJCdjZ6TiQ=='
|
72
72
|
# }
|
73
|
-
def self.generate_data_key(version: 0,
|
73
|
+
def self.generate_data_key(cipher_name:, app_name:, environment:, key_path:, version: 0,
|
74
74
|
regions: Utils::Aws::AWS_US_REGIONS,
|
75
75
|
dek: nil,
|
76
|
-
|
77
|
-
app_name:,
|
78
|
-
environment:,
|
79
|
-
key_path:,
|
80
|
-
**args)
|
76
|
+
**_args)
|
81
77
|
|
82
78
|
# TODO: Also support generating environment variables instead of files.
|
83
79
|
|
84
80
|
version >= 255 ? (version = 1) : (version += 1)
|
85
|
-
regions
|
81
|
+
regions = Array(regions).dup
|
86
82
|
|
87
83
|
master_key_alias = master_key_alias(app_name, environment)
|
88
84
|
|
89
85
|
# File per region for holding the encrypted data key
|
90
|
-
key_files
|
86
|
+
key_files = regions.collect do |region|
|
91
87
|
file_name = "#{app_name}_#{environment}_#{region}_v#{version}.encrypted_key"
|
92
88
|
{region: region, file_name: ::File.join(key_path, file_name)}
|
93
89
|
end
|
@@ -116,12 +112,13 @@ module SymmetricEncryption
|
|
116
112
|
|
117
113
|
# Stores the Encryption key in a file.
|
118
114
|
# Secures the Encryption key by encrypting it with a key encryption key.
|
119
|
-
def initialize(region: nil,
|
115
|
+
def initialize(key_files:, master_key_alias:, region: nil, key_encrypting_key: nil)
|
120
116
|
@key_files = key_files
|
121
117
|
@master_key_alias = master_key_alias
|
122
|
-
@region = region || ENV[
|
118
|
+
@region = region || ENV["AWS_REGION"] || ENV["AWS_DEFAULT_REGION"] || ::Aws.config[:region]
|
123
119
|
if key_encrypting_key
|
124
|
-
raise(SymmetricEncryption::ConfigError,
|
120
|
+
raise(SymmetricEncryption::ConfigError,
|
121
|
+
"AWS KMS keystore encrypts the key itself, so does not support supplying a key_encrypting_key")
|
125
122
|
end
|
126
123
|
end
|
127
124
|
|
@@ -143,7 +140,7 @@ module SymmetricEncryption
|
|
143
140
|
region = key_file[:region]
|
144
141
|
file_name = key_file[:file_name]
|
145
142
|
|
146
|
-
raise(ArgumentError,
|
143
|
+
raise(ArgumentError, "region and file_name are mandatory for each key_file entry") unless region && file_name
|
147
144
|
|
148
145
|
encrypted_data_key = aws(region).encrypt(data_key)
|
149
146
|
write_encoded_to_file(file_name, encrypted_data_key)
|
@@ -7,13 +7,13 @@ module SymmetricEncryption
|
|
7
7
|
# Returns [Hash] a new keystore configuration after generating the data key.
|
8
8
|
#
|
9
9
|
# Increments the supplied version number by 1.
|
10
|
-
def self.generate_data_key(cipher_name:, app_name:, environment:, version: 0, dek: nil, **
|
10
|
+
def self.generate_data_key(cipher_name:, app_name:, environment:, version: 0, dek: nil, **_args)
|
11
11
|
version >= 255 ? (version = 1) : (version += 1)
|
12
12
|
|
13
|
-
kek
|
13
|
+
kek = SymmetricEncryption::Key.new(cipher_name: cipher_name)
|
14
14
|
dek ||= SymmetricEncryption::Key.new(cipher_name: cipher_name)
|
15
15
|
|
16
|
-
key_env_var = "#{app_name}_#{environment}_v#{version}".upcase.tr(
|
16
|
+
key_env_var = "#{app_name}_#{environment}_v#{version}".upcase.tr("-", "_")
|
17
17
|
new(key_env_var: key_env_var, key_encrypting_key: kek).write(dek.key)
|
18
18
|
|
19
19
|
{
|
@@ -50,9 +50,9 @@ module SymmetricEncryption
|
|
50
50
|
def write(key)
|
51
51
|
encrypted_key = key_encrypting_key.encrypt(key)
|
52
52
|
puts "\n\n********************************************************************************"
|
53
|
-
puts
|
53
|
+
puts "Set the environment variable as follows:"
|
54
54
|
puts " export #{key_env_var}=\"#{encoder.encode(encrypted_key)}\""
|
55
|
-
puts
|
55
|
+
puts "********************************************************************************"
|
56
56
|
end
|
57
57
|
|
58
58
|
private
|
@@ -2,17 +2,18 @@ module SymmetricEncryption
|
|
2
2
|
module Keystore
|
3
3
|
class File
|
4
4
|
include Utils::Files
|
5
|
+
ALLOWED_PERMISSIONS = %w[100600 100400].freeze
|
5
6
|
|
6
7
|
attr_accessor :file_name, :key_encrypting_key
|
7
8
|
|
8
9
|
# Returns [Hash] a new keystore configuration after generating the data key.
|
9
10
|
#
|
10
11
|
# Increments the supplied version number by 1.
|
11
|
-
def self.generate_data_key(key_path:, cipher_name:, app_name:, environment:, version: 0, dek: nil, **
|
12
|
+
def self.generate_data_key(key_path:, cipher_name:, app_name:, environment:, version: 0, dek: nil, **_args)
|
12
13
|
version >= 255 ? (version = 1) : (version += 1)
|
13
14
|
|
14
15
|
dek ||= SymmetricEncryption::Key.new(cipher_name: cipher_name)
|
15
|
-
kek
|
16
|
+
kek = SymmetricEncryption::Key.new(cipher_name: cipher_name)
|
16
17
|
kekek = SymmetricEncryption::Key.new(cipher_name: cipher_name)
|
17
18
|
|
18
19
|
dek_file_name = ::File.join(key_path, "#{app_name}_#{environment}_v#{version}.encrypted_key")
|
@@ -47,11 +48,22 @@ module SymmetricEncryption
|
|
47
48
|
|
48
49
|
# Returns the Encryption key in the clear.
|
49
50
|
def read
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
unless ::File.exist?(file_name)
|
52
|
+
raise(SymmetricEncryption::ConfigError,
|
53
|
+
"Symmetric Encryption key file: '#{file_name}' not found")
|
54
|
+
end
|
55
|
+
unless correct_permissions?
|
56
|
+
raise(SymmetricEncryption::ConfigError,
|
57
|
+
"Symmetric Encryption key file '#{file_name}' has the wrong "\
|
58
|
+
"permissions: #{::File.stat(file_name).mode.to_s(8)}. Expected 100600 or 100400.")
|
59
|
+
end
|
60
|
+
unless owned?
|
61
|
+
raise(SymmetricEncryption::ConfigError,
|
62
|
+
"Symmetric Encryption key file '#{file_name}' has the wrong "\
|
63
|
+
"owner (#{stat.uid}) or group (#{stat.gid}). "\
|
64
|
+
"Expected it to be owned by current user "\
|
65
|
+
"#{ENV['USER'] || ENV['USERNAME']}.")
|
66
|
+
end
|
55
67
|
|
56
68
|
data = read_from_file(file_name)
|
57
69
|
key_encrypting_key ? key_encrypting_key.decrypt(data) : data
|
@@ -69,9 +81,15 @@ module SymmetricEncryption
|
|
69
81
|
# has the correct mode - readable and writable by its owner and no one
|
70
82
|
# else, much like the keys one has in ~/.ssh
|
71
83
|
def correct_permissions?
|
72
|
-
stat
|
84
|
+
ALLOWED_PERMISSIONS.include?(stat.mode.to_s(8))
|
85
|
+
end
|
86
|
+
|
87
|
+
def owned?
|
88
|
+
stat.owned?
|
89
|
+
end
|
73
90
|
|
74
|
-
|
91
|
+
def stat
|
92
|
+
::File.stat(file_name)
|
75
93
|
end
|
76
94
|
end
|
77
95
|
end
|
@@ -5,12 +5,12 @@ module SymmetricEncryption
|
|
5
5
|
class Gcp
|
6
6
|
include Utils::Files
|
7
7
|
|
8
|
-
def self.generate_data_key(
|
8
|
+
def self.generate_data_key(cipher_name:, app_name:, environment:, key_path:, version: 0)
|
9
9
|
version >= 255 ? (version = 1) : (version += 1)
|
10
10
|
|
11
|
-
dek
|
11
|
+
dek = SymmetricEncryption::Key.new(cipher_name: cipher_name)
|
12
12
|
file_name = "#{key_path}/#{app_name}_#{environment}_v#{version}.encrypted_key"
|
13
|
-
keystore
|
13
|
+
keystore = new(
|
14
14
|
key_file: file_name,
|
15
15
|
app_name: app_name,
|
16
16
|
environment: environment
|
@@ -18,21 +18,21 @@ module SymmetricEncryption
|
|
18
18
|
keystore.write(dek.key)
|
19
19
|
|
20
20
|
{
|
21
|
-
keystore:
|
22
|
-
cipher_name:
|
23
|
-
version:
|
24
|
-
key_file:
|
25
|
-
iv:
|
26
|
-
crypto_key:
|
21
|
+
keystore: :gcp,
|
22
|
+
cipher_name: dek.cipher_name,
|
23
|
+
version: version,
|
24
|
+
key_file: file_name,
|
25
|
+
iv: dek.iv,
|
26
|
+
crypto_key: keystore.crypto_key
|
27
27
|
}
|
28
28
|
end
|
29
29
|
|
30
30
|
def initialize(key_file:, app_name: nil, environment: nil, key_encrypting_key: nil, crypto_key: nil, project_id: nil, credentials: nil, location_id: nil)
|
31
|
-
@crypto_key
|
32
|
-
@app_name
|
31
|
+
@crypto_key = crypto_key
|
32
|
+
@app_name = app_name
|
33
33
|
@environment = environment
|
34
|
-
@file_name
|
35
|
-
@project_id
|
34
|
+
@file_name = key_file
|
35
|
+
@project_id = project_id
|
36
36
|
@credentials = credentials
|
37
37
|
@location_id = location_id
|
38
38
|
end
|
@@ -46,7 +46,8 @@ module SymmetricEncryption
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def crypto_key
|
49
|
-
@crypto_key ||= self.class::KMS::KeyManagementServiceClient.crypto_key_path(project_id, location_id, app_name,
|
49
|
+
@crypto_key ||= self.class::KMS::KeyManagementServiceClient.crypto_key_path(project_id, location_id, app_name,
|
50
|
+
environment.to_s)
|
50
51
|
end
|
51
52
|
|
52
53
|
private
|
@@ -69,18 +70,20 @@ module SymmetricEncryption
|
|
69
70
|
|
70
71
|
def project_id
|
71
72
|
@project_id ||= ENV["GOOGLE_CLOUD_PROJECT"]
|
72
|
-
raise
|
73
|
+
raise "GOOGLE_CLOUD_PROJECT must be set" if @project_id.nil?
|
74
|
+
|
73
75
|
@project_id
|
74
76
|
end
|
75
77
|
|
76
78
|
def credentials
|
77
|
-
@credentials ||= ENV[
|
78
|
-
raise
|
79
|
+
@credentials ||= ENV["GOOGLE_CLOUD_KEYFILE"]
|
80
|
+
raise "GOOGLE_CLOUD_KEYFILE must be set" if @credentials.nil?
|
81
|
+
|
79
82
|
@credentials
|
80
83
|
end
|
81
84
|
|
82
85
|
def location_id
|
83
|
-
@location_id ||= ENV["GOOGLE_CLOUD_LOCATION"] ||
|
86
|
+
@location_id ||= ENV["GOOGLE_CLOUD_LOCATION"] || "global"
|
84
87
|
end
|
85
88
|
end
|
86
89
|
end
|