symmetric-encryption 4.2.1 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) 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/{railties → active_record}/attr_encrypted.rb +16 -5
  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 +72 -54
  10. data/lib/symmetric_encryption/coerce.rb +3 -3
  11. data/lib/symmetric_encryption/config.rb +28 -27
  12. data/lib/symmetric_encryption/core.rb +25 -20
  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 +10 -13
  18. data/lib/symmetric_encryption/keystore/environment.rb +5 -5
  19. data/lib/symmetric_encryption/keystore/file.rb +27 -9
  20. data/lib/symmetric_encryption/keystore/gcp.rb +21 -18
  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 -23
  24. data/lib/symmetric_encryption/railtie.rb +12 -11
  25. data/lib/symmetric_encryption/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 +3 -3
  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 +13 -9
  36. metadata +10 -10
@@ -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
@@ -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, '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 '#'
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['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
55
+ env ||= defined?(Rails) ? Rails.env : ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
56
56
 
57
57
  unless file_name
58
- root = defined?(Rails) ? Rails.root : '.'
59
- file_name =
60
- if (env_var = ENV['SYMMETRIC_ENCRYPTION_CONFIG'])
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, 'config', 'symmetric-encryption.yml')
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 ||= begin
75
- 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)
76
77
 
77
- env_config = YAML.load(ERB.new(File.new(file_name).read).result)[env]
78
- raise(ConfigError, "Cannot find environment: #{env} in config file: #{file_name}") unless env_config
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
- env_config = self.class.send(:deep_symbolize_keys, env_config)
81
- self.class.send(:migrate_old_formats!, env_config)
82
- end
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(SymmetricEncrytion::Cipher)] ciphers specified in the configuration file.
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] = [inline_cipher]
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 = config.delete(: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] = old_key_name_cipher
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 '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."
159
160
  end
160
161
  end
161
162
  config
@@ -1,30 +1,35 @@
1
1
  # Used for compression
2
- require 'zlib'
2
+ require "zlib"
3
3
  # Used to coerce data types between string and their actual types
4
- require 'coercible'
4
+ require "coercible"
5
5
 
6
- require 'symmetric_encryption/version'
7
- require 'symmetric_encryption/cipher'
8
- require 'symmetric_encryption/symmetric_encryption'
9
- require 'symmetric_encryption/exception'
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, 'symmetric_encryption/coerce'
14
- autoload :Config, 'symmetric_encryption/config'
15
- autoload :Encoder, 'symmetric_encryption/encoder'
16
- autoload :Generator, 'symmetric_encryption/generator'
17
- autoload :Header, 'symmetric_encryption/header'
18
- autoload :Key, 'symmetric_encryption/key'
19
- autoload :Reader, 'symmetric_encryption/reader'
20
- autoload :RSAKey, 'symmetric_encryption/rsa_key'
21
- autoload :Writer, 'symmetric_encryption/writer'
22
- autoload :CLI, 'symmetric_encryption/cli'
23
- autoload :Keystore, 'symmetric_encryption/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
+ module ActiveRecord
26
+ autoload :EncryptedAttribute, "symmetric_encryption/active_record/encrypted_attribute"
27
+ end
28
+
24
29
  module Utils
25
- autoload :Aws, 'symmetric_encryption/utils/aws'
26
- autoload :Files, 'symmetric_encryption/utils/files'
27
- autoload :ReEncryptFiles, 'symmetric_encryption/utils/re_encrypt_files'
30
+ autoload :Aws, "symmetric_encryption/utils/aws"
31
+ autoload :Files, "symmetric_encryption/utils/files"
32
+ autoload :ReEncryptFiles, "symmetric_encryption/utils/re_encrypt_files"
28
33
  end
29
34
  end
30
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)
@@ -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 = '@EnC'.force_encoding(SymmetricEncryption::BINARY_ENCODING)
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 == '') || (buffer.length <= MAGIC_HEADER_SIZE + 2)
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 += 1
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 = buffer.getbyte(offset)
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) != 0
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) != 0
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) != 0
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) != 0
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 = 0
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('v')
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('v')
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('v')
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('v')
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 = buffer.byteslice(offset, 2).unpack('v').first
261
+ len = buffer.byteslice(offset, 2).unpack("v").first
262
262
  offset += 2
263
- out = buffer.byteslice(offset, len)
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: 'aes-256-cbc')
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 'aws-sdk-kms'
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
- cipher_name:,
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 = Array(regions).dup
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 = regions.collect do |region|
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, key_files:, master_key_alias:, key_encrypting_key: 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['AWS_REGION'] || ENV['AWS_DEFAULT_REGION'] || ::Aws.config[:region]
118
+ @region = region || ENV["AWS_REGION"] || ENV["AWS_DEFAULT_REGION"] || ::Aws.config[:region]
123
119
  if key_encrypting_key
124
- raise(SymmetricEncryption::ConfigError, 'AWS KMS keystore encrypts the key itself, so does not support supplying a key_encrypting_key')
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, 'region and file_name are mandatory for each key_file entry') unless region && file_name
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, **args)
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 = SymmetricEncryption::Key.new(cipher_name: cipher_name)
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 'Set the environment variable as follows:'
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, **args)
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 = SymmetricEncryption::Key.new(cipher_name: cipher_name)
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
- raise(SymmetricEncryption::ConfigError,
51
- "Symmetric Encryption key file: '#{file_name}' not found") unless ::File.exists?(file_name)
52
- raise(SymmetricEncryption::ConfigError,
53
- "Symmetric Encryption key file '#{file_name}' has the wrong "\
54
- "permissions: #{::File.stat(file_name).mode.to_s(8)}. Expected 100600 or 100400.") unless correct_permissions?
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 = ::File.stat(file_name)
84
+ ALLOWED_PERMISSIONS.include?(stat.mode.to_s(8))
85
+ end
86
+
87
+ def owned?
88
+ stat.owned?
89
+ end
73
90
 
74
- stat.owned? && %w(100600 100400).include?(stat.mode.to_s(8))
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(version: 0, cipher_name:, app_name:, environment:, key_path:)
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 = SymmetricEncryption::Key.new(cipher_name: cipher_name)
11
+ dek = SymmetricEncryption::Key.new(cipher_name: cipher_name)
12
12
  file_name = "#{key_path}/#{app_name}_#{environment}_v#{version}.encrypted_key"
13
- keystore = new(
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: :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
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 = crypto_key
32
- @app_name = app_name
31
+ @crypto_key = crypto_key
32
+ @app_name = app_name
33
33
  @environment = environment
34
- @file_name = key_file
35
- @project_id = 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, environment.to_s)
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 'GOOGLE_CLOUD_PROJECT must be set' if @project_id.nil?
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['GOOGLE_CLOUD_KEYFILE']
78
- raise 'GOOGLE_CLOUD_KEYFILE must be set' if @credentials.nil?
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"] || 'global'
86
+ @location_id ||= ENV["GOOGLE_CLOUD_LOCATION"] || "global"
84
87
  end
85
88
  end
86
89
  end