symmetric-encryption 4.1.4 → 4.2.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/lib/symmetric_encryption.rb +11 -48
- data/lib/symmetric_encryption/cli.rb +2 -2
- data/lib/symmetric_encryption/core.rb +30 -0
- data/lib/symmetric_encryption/keystore.rb +1 -0
- data/lib/symmetric_encryption/keystore/aws.rb +4 -19
- data/lib/symmetric_encryption/keystore/file.rb +4 -17
- data/lib/symmetric_encryption/keystore/gcp.rb +87 -0
- data/lib/symmetric_encryption/railties/attr_encrypted.rb +118 -0
- data/lib/symmetric_encryption/{extensions/mongoid/encrypted.rb → railties/mongoid_encrypted.rb} +0 -0
- data/lib/symmetric_encryption/utils/files.rb +45 -0
- data/lib/symmetric_encryption/version.rb +1 -1
- metadata +7 -5
- data/lib/symmetric_encryption/extensions/active_record/base.rb +0 -110
- data/lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a62a8497a82ccb4701f2301bc351ab8e054569a3cdc02a0e0f2e14cc9866776
|
4
|
+
data.tar.gz: f8867d2e9ced1fd3f7adb081a851481cb3abc11f067d67104324bce539911e26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8feb0c726ca19c1a956c24b1ec74c11786e07dc1d5131741697b23c0ff9a4b692a3b24853040b73e1418321a761d7f0687f51f620332c7480be73f5b23935fb
|
7
|
+
data.tar.gz: 26fb41fd4d2dc609eb21c382399ed1656798071eedcef94f127e4f4054f7c46f268b94c344ed73cf4ea51583dcbff08deb602df02c04b3dfd5e8fb6a3ea0b73d
|
data/lib/symmetric_encryption.rb
CHANGED
@@ -1,32 +1,4 @@
|
|
1
|
-
|
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 :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'
|
24
|
-
module Utils
|
25
|
-
autoload :Aws, 'symmetric_encryption/utils/aws'
|
26
|
-
autoload :ReEncryptFiles, 'symmetric_encryption/utils/re_encrypt_files'
|
27
|
-
end
|
28
|
-
end
|
29
|
-
# @formatter:on
|
1
|
+
require 'symmetric_encryption/core'
|
30
2
|
|
31
3
|
# Add extensions. Gems are no longer order dependent.
|
32
4
|
begin
|
@@ -36,26 +8,17 @@ rescue LoadError
|
|
36
8
|
end
|
37
9
|
|
38
10
|
begin
|
39
|
-
require '
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
begin
|
45
|
-
require 'active_model'
|
46
|
-
require 'symmetric_encryption/railties/symmetric_encryption_validator'
|
47
|
-
rescue LoadError
|
48
|
-
end
|
11
|
+
require 'active_support'
|
12
|
+
ActiveSupport.on_load(:active_record) do
|
13
|
+
require 'symmetric_encryption/railties/attr_encrypted'
|
14
|
+
require 'symmetric_encryption/railties/symmetric_encryption_validator'
|
49
15
|
|
50
|
-
|
51
|
-
|
52
|
-
require 'symmetric_encryption/extensions/mongoid/encrypted'
|
53
|
-
rescue LoadError
|
54
|
-
end
|
16
|
+
ActiveRecord::Base.include(SymmetricEncryption::Railties::AttrEncrypted)
|
17
|
+
end
|
55
18
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
19
|
+
ActiveSupport.on_load(:mongoid) do
|
20
|
+
require 'symmetric_encryption/railties/mongoid_encrypted'
|
21
|
+
require 'symmetric_encryption/railties/symmetric_encryption_validator'
|
22
|
+
end
|
60
23
|
rescue LoadError
|
61
24
|
end
|
@@ -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[aws 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!
|
@@ -131,7 +131,7 @@ module SymmetricEncryption
|
|
131
131
|
@generate = config
|
132
132
|
end
|
133
133
|
|
134
|
-
opts.on '-s', '--keystore heroku|environment|file|aws', 'Which keystore to use during generation or re-encryption.' do |keystore|
|
134
|
+
opts.on '-s', '--keystore heroku|environment|file|aws|gcp', 'Which keystore to use during generation or re-encryption.' do |keystore|
|
135
135
|
@keystore = (keystore || 'file').downcase.to_sym
|
136
136
|
end
|
137
137
|
|
@@ -0,0 +1,30 @@
|
|
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 :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'
|
24
|
+
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'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
# @formatter:on
|
@@ -4,6 +4,7 @@ module SymmetricEncryption
|
|
4
4
|
# @formatter:off
|
5
5
|
autoload :Aws, 'symmetric_encryption/keystore/aws'
|
6
6
|
autoload :Environment, 'symmetric_encryption/keystore/environment'
|
7
|
+
autoload :Gcp, 'symmetric_encryption/keystore/gcp'
|
7
8
|
autoload :File, 'symmetric_encryption/keystore/file'
|
8
9
|
autoload :Heroku, 'symmetric_encryption/keystore/heroku'
|
9
10
|
autoload :Memory, 'symmetric_encryption/keystore/memory'
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'base64'
|
2
1
|
require 'aws-sdk-kms'
|
3
2
|
module SymmetricEncryption
|
4
3
|
module Keystore
|
@@ -51,6 +50,8 @@ module SymmetricEncryption
|
|
51
50
|
# - Loss of access to AWS accounts.
|
52
51
|
# - Loss of region(s) in which master keys are stored.
|
53
52
|
class Aws
|
53
|
+
include Utils::Files
|
54
|
+
|
54
55
|
attr_reader :region, :key_files, :master_key_alias
|
55
56
|
|
56
57
|
# Returns [Hash] a new keystore configuration after generating the data key.
|
@@ -131,13 +132,8 @@ module SymmetricEncryption
|
|
131
132
|
raise(SymmetricEncryption::ConfigError, "region: #{region} not available in the supplied key_files") unless key_file
|
132
133
|
|
133
134
|
file_name = key_file[:file_name]
|
134
|
-
raise(SymmetricEncryption::ConfigError, 'file_name is mandatory for each key_file entry') unless file_name
|
135
|
-
|
136
|
-
raise(SymmetricEncryption::ConfigError, "File #{file_name} could not be found") unless ::File.exist?(file_name)
|
137
135
|
|
138
|
-
|
139
|
-
encoded_dek = ::File.open(file_name, 'rb', &:read)
|
140
|
-
encrypted_data_key = Base64.strict_decode64(encoded_dek)
|
136
|
+
encrypted_data_key = read_file_and_decode(file_name)
|
141
137
|
aws(region).decrypt(encrypted_data_key)
|
142
138
|
end
|
143
139
|
|
@@ -150,24 +146,13 @@ module SymmetricEncryption
|
|
150
146
|
raise(ArgumentError, 'region and file_name are mandatory for each key_file entry') unless region && file_name
|
151
147
|
|
152
148
|
encrypted_data_key = aws(region).encrypt(data_key)
|
153
|
-
|
154
|
-
write_to_file(file_name, encoded_dek)
|
149
|
+
write_encoded_to_file(file_name, encrypted_data_key)
|
155
150
|
end
|
156
151
|
end
|
157
152
|
|
158
153
|
def aws(region)
|
159
154
|
Utils::Aws.new(region: region, master_key_alias: master_key_alias)
|
160
155
|
end
|
161
|
-
|
162
|
-
private
|
163
|
-
|
164
|
-
# Write to the supplied file_name, backing up the existing file if present
|
165
|
-
def write_to_file(file_name, data)
|
166
|
-
path = ::File.dirname(file_name)
|
167
|
-
::FileUtils.mkdir_p(path) unless ::File.directory?(path)
|
168
|
-
::File.rename(file_name, "#{file_name}.#{Time.now.to_i}") if ::File.exist?(file_name)
|
169
|
-
::File.open(file_name, 'wb') { |file| file.write(data) }
|
170
|
-
end
|
171
156
|
end
|
172
157
|
end
|
173
158
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module SymmetricEncryption
|
2
2
|
module Keystore
|
3
3
|
class File
|
4
|
+
include Utils::Files
|
5
|
+
|
4
6
|
attr_accessor :file_name, :key_encrypting_key
|
5
7
|
|
6
8
|
# Returns [Hash] a new keystore configuration after generating the data key.
|
@@ -51,33 +53,18 @@ module SymmetricEncryption
|
|
51
53
|
"Symmetric Encryption key file '#{file_name}' has the wrong "\
|
52
54
|
"permissions: #{::File.stat(file_name).mode.to_s(8)}. Expected 100600.") unless correct_permissions?
|
53
55
|
|
54
|
-
data = read_from_file
|
56
|
+
data = read_from_file(file_name)
|
55
57
|
key_encrypting_key ? key_encrypting_key.decrypt(data) : data
|
56
58
|
end
|
57
59
|
|
58
60
|
# Encrypt and write the key to file.
|
59
61
|
def write(key)
|
60
62
|
data = key_encrypting_key ? key_encrypting_key.encrypt(key) : key
|
61
|
-
write_to_file(data)
|
63
|
+
write_to_file(file_name, data)
|
62
64
|
end
|
63
65
|
|
64
66
|
private
|
65
67
|
|
66
|
-
# Read from the file, raising an exception if it is not found
|
67
|
-
def read_from_file
|
68
|
-
::File.open(file_name, 'rb', &:read)
|
69
|
-
rescue Errno::ENOENT
|
70
|
-
raise(SymmetricEncryption::ConfigError, "Symmetric Encryption key file: '#{file_name}' not found or readable")
|
71
|
-
end
|
72
|
-
|
73
|
-
# Write to the supplied file_name, backing up the existing file if present
|
74
|
-
def write_to_file(data)
|
75
|
-
key_path = ::File.dirname(file_name)
|
76
|
-
::FileUtils.mkdir_p(key_path) unless ::File.directory?(key_path)
|
77
|
-
::File.rename(file_name, "#{file_name}.#{Time.now.to_i}") if ::File.exist?(file_name)
|
78
|
-
::File.open(file_name, 'wb', 0600) { |file| file.write(data) }
|
79
|
-
end
|
80
|
-
|
81
68
|
# Returns true if the file is owned by the user running this code and it
|
82
69
|
# has the correct mode - readable and writable by its owner and no one
|
83
70
|
# else, much like the keys one has in ~/.ssh
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "google/cloud/kms/v1"
|
2
|
+
|
3
|
+
module SymmetricEncryption
|
4
|
+
module Keystore
|
5
|
+
class Gcp
|
6
|
+
include Utils::Files
|
7
|
+
|
8
|
+
def self.generate_data_key(version: 0, cipher_name:, app_name:, environment:, key_path:)
|
9
|
+
version >= 255 ? (version = 1) : (version += 1)
|
10
|
+
|
11
|
+
dek = SymmetricEncryption::Key.new(cipher_name: cipher_name)
|
12
|
+
file_name = "#{key_path}/#{app_name}_#{environment}_v#{version}.encrypted_key"
|
13
|
+
keystore = new(
|
14
|
+
key_file: file_name,
|
15
|
+
app_name: app_name,
|
16
|
+
environment: environment
|
17
|
+
)
|
18
|
+
keystore.write(dek.key)
|
19
|
+
|
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
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
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
|
33
|
+
@environment = environment
|
34
|
+
@file_name = key_file
|
35
|
+
@project_id = project_id
|
36
|
+
@credentials = credentials
|
37
|
+
@location_id = location_id
|
38
|
+
end
|
39
|
+
|
40
|
+
def read
|
41
|
+
decrypt(read_file_and_decode(file_name))
|
42
|
+
end
|
43
|
+
|
44
|
+
def write(data_key)
|
45
|
+
write_encoded_to_file(file_name, encrypt(data_key))
|
46
|
+
end
|
47
|
+
|
48
|
+
def crypto_key
|
49
|
+
@crypto_key ||= self.class::KMS::KeyManagementServiceClient.crypto_key_path(project_id, location_id, app_name, environment.to_s)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
KMS = Google::Cloud::Kms::V1
|
55
|
+
|
56
|
+
attr_reader :app_name, :environment
|
57
|
+
|
58
|
+
def encrypt(plaintext)
|
59
|
+
client.encrypt(crypto_key, plaintext).ciphertext
|
60
|
+
end
|
61
|
+
|
62
|
+
def decrypt(ciphertext)
|
63
|
+
client.decrypt(crypto_key, ciphertext).plaintext
|
64
|
+
end
|
65
|
+
|
66
|
+
def client
|
67
|
+
self.class::KMS::KeyManagementServiceClient.new(timeout: 2, credentials: credentials)
|
68
|
+
end
|
69
|
+
|
70
|
+
def project_id
|
71
|
+
@project_id ||= ENV["GOOGLE_CLOUD_PROJECT"]
|
72
|
+
raise 'GOOGLE_CLOUD_PROJECT must be set' if @project_id.nil?
|
73
|
+
@project_id
|
74
|
+
end
|
75
|
+
|
76
|
+
def credentials
|
77
|
+
@credentials ||= ENV['GOOGLE_CLOUD_KEYFILE']
|
78
|
+
raise 'GOOGLE_CLOUD_KEYFILE must be set' if @credentials.nil?
|
79
|
+
@credentials
|
80
|
+
end
|
81
|
+
|
82
|
+
def location_id
|
83
|
+
@location_id ||= ENV["GOOGLE_CLOUD_LOCATION"] || 'global'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module SymmetricEncryption
|
2
|
+
module Railties
|
3
|
+
module AttrEncrypted
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
# Transparently encrypt and decrypt values stored via ActiveRecord.
|
10
|
+
#
|
11
|
+
# Parameters:
|
12
|
+
# * Symbolic names of each method to create which has a corresponding
|
13
|
+
# method already defined in rails starting with: encrypted_
|
14
|
+
# * Followed by an optional hash:
|
15
|
+
# :random_iv [true|false]
|
16
|
+
# Whether the encrypted value should use a random IV every time the
|
17
|
+
# field is encrypted.
|
18
|
+
# It is recommended to set this to true where feasible. If the encrypted
|
19
|
+
# value could be used as part of a SQL where clause, or as part
|
20
|
+
# of any lookup, then it must be false.
|
21
|
+
# Setting random_iv to true will result in a different encrypted output for
|
22
|
+
# the same input string.
|
23
|
+
# Note: Only set to true if the field will never be used as part of
|
24
|
+
# the where clause in an SQL query.
|
25
|
+
# Note: When random_iv is true it will add a 8 byte header, plus the bytes
|
26
|
+
# to store the random IV in every returned encrypted string, prior to the
|
27
|
+
# encoding if any.
|
28
|
+
# Default: false
|
29
|
+
# Highly Recommended where feasible: true
|
30
|
+
#
|
31
|
+
# :type [Symbol]
|
32
|
+
# The type for this field, #see SymmetricEncryption::COERCION_TYPES
|
33
|
+
# Default: :string
|
34
|
+
#
|
35
|
+
# :compress [true|false]
|
36
|
+
# Whether to compress str before encryption
|
37
|
+
# Should only be used for large strings since compression overhead and
|
38
|
+
# the overhead of adding the 'magic' header may exceed any benefits of
|
39
|
+
# compression
|
40
|
+
# Note: Adds a 6 byte header prior to encoding, only if :random_iv is false
|
41
|
+
# Default: false
|
42
|
+
def attr_encrypted(*params)
|
43
|
+
# Ensure ActiveRecord has created all its methods first
|
44
|
+
# Ignore failures since the table may not yet actually exist
|
45
|
+
begin
|
46
|
+
define_attribute_methods
|
47
|
+
rescue StandardError
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
options = params.last.is_a?(Hash) ? params.pop.dup : {}
|
52
|
+
|
53
|
+
params.each do |attribute|
|
54
|
+
SymmetricEncryption::Generator.generate_decrypted_accessors(self, attribute, "encrypted_#{attribute}", options)
|
55
|
+
encrypted_attributes[attribute.to_sym] = "encrypted_#{attribute}".to_sym
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Contains a hash of encrypted attributes with virtual attribute names as keys and real attribute
|
60
|
+
# names as values
|
61
|
+
#
|
62
|
+
# Example
|
63
|
+
#
|
64
|
+
# class User < ActiveRecord::Base
|
65
|
+
# attr_encrypted :email
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# User.encrypted_attributes => { email: encrypted_email }
|
69
|
+
def encrypted_attributes
|
70
|
+
@encrypted_attributes ||= superclass.respond_to?(:encrypted_attributes) ? superclass.encrypted_attributes.dup : {}
|
71
|
+
end
|
72
|
+
|
73
|
+
# Return the name of all encrypted virtual attributes as an Array of symbols
|
74
|
+
# Example: [:email, :password]
|
75
|
+
def encrypted_keys
|
76
|
+
@encrypted_keys ||= encrypted_attributes.keys
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return the name of all encrypted columns as an Array of symbols
|
80
|
+
# Example: [:encrypted_email, :encrypted_password]
|
81
|
+
def encrypted_columns
|
82
|
+
@encrypted_columns ||= encrypted_attributes.values
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns whether an attribute has been configured to be encrypted
|
86
|
+
#
|
87
|
+
# Example
|
88
|
+
#
|
89
|
+
# class User < ActiveRecord::Base
|
90
|
+
# attr_accessor :name
|
91
|
+
# attr_encrypted :email
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# User.encrypted_attribute?(:name) # false
|
95
|
+
# User.encrypted_attribute?(:email) # true
|
96
|
+
def encrypted_attribute?(attribute)
|
97
|
+
encrypted_keys.include?(attribute)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns whether the attribute is the database column to hold the
|
101
|
+
# encrypted data for a matching encrypted attribute
|
102
|
+
#
|
103
|
+
# Example
|
104
|
+
#
|
105
|
+
# class User < ActiveRecord::Base
|
106
|
+
# attr_accessor :name
|
107
|
+
# attr_encrypted :email
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# User.encrypted_column?(:encrypted_name) # false
|
111
|
+
# User.encrypted_column?(:encrypted_email) # true
|
112
|
+
def encrypted_column?(attribute)
|
113
|
+
encrypted_columns.include?(attribute)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/symmetric_encryption/{extensions/mongoid/encrypted.rb → railties/mongoid_encrypted.rb}
RENAMED
File without changes
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module SymmetricEncryption
|
2
|
+
module Utils
|
3
|
+
module Files
|
4
|
+
private
|
5
|
+
|
6
|
+
attr_reader :file_name
|
7
|
+
|
8
|
+
def read_file_and_decode(file_name)
|
9
|
+
raise(SymmetricEncryption::ConfigError, 'file_name is mandatory for each key_file entry') unless file_name
|
10
|
+
|
11
|
+
raise(SymmetricEncryption::ConfigError, "File #{file_name} could not be found") unless ::File.exist?(file_name)
|
12
|
+
|
13
|
+
# TODO: Validate that file is not globally readable.
|
14
|
+
decode64(read_from_file(file_name))
|
15
|
+
end
|
16
|
+
|
17
|
+
def write_encoded_to_file(file_name, encrypted_data_key)
|
18
|
+
write_to_file(file_name, encode64(encrypted_data_key))
|
19
|
+
end
|
20
|
+
|
21
|
+
def encode64(data)
|
22
|
+
Base64.strict_encode64(data)
|
23
|
+
end
|
24
|
+
|
25
|
+
def decode64(data)
|
26
|
+
Base64.strict_decode64(data)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Write to the supplied file_name, backing up the existing file if present
|
30
|
+
def write_to_file(file_name, data)
|
31
|
+
key_path = ::File.dirname(file_name)
|
32
|
+
::FileUtils.mkdir_p(key_path) unless ::File.directory?(key_path)
|
33
|
+
::File.rename(file_name, "#{file_name}.#{Time.now.to_i}") if ::File.exist?(file_name)
|
34
|
+
::File.open(file_name, 'wb', 0600) { |file| file.write(data) }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Read from the file, raising an exception if it is not found
|
38
|
+
def read_from_file(file_name)
|
39
|
+
::File.open(file_name, 'rb', &:read)
|
40
|
+
rescue Errno::ENOENT
|
41
|
+
raise(SymmetricEncryption::ConfigError, "Symmetric Encryption key file: '#{file_name}' not found or readable")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: symmetric-encryption
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reid Morrison
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coercible
|
@@ -42,11 +42,9 @@ files:
|
|
42
42
|
- lib/symmetric_encryption/cli.rb
|
43
43
|
- lib/symmetric_encryption/coerce.rb
|
44
44
|
- lib/symmetric_encryption/config.rb
|
45
|
+
- lib/symmetric_encryption/core.rb
|
45
46
|
- lib/symmetric_encryption/encoder.rb
|
46
47
|
- lib/symmetric_encryption/exception.rb
|
47
|
-
- lib/symmetric_encryption/extensions/active_record/base.rb
|
48
|
-
- lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb
|
49
|
-
- lib/symmetric_encryption/extensions/mongoid/encrypted.rb
|
50
48
|
- lib/symmetric_encryption/generator.rb
|
51
49
|
- lib/symmetric_encryption/header.rb
|
52
50
|
- lib/symmetric_encryption/key.rb
|
@@ -54,14 +52,18 @@ files:
|
|
54
52
|
- lib/symmetric_encryption/keystore/aws.rb
|
55
53
|
- lib/symmetric_encryption/keystore/environment.rb
|
56
54
|
- lib/symmetric_encryption/keystore/file.rb
|
55
|
+
- lib/symmetric_encryption/keystore/gcp.rb
|
57
56
|
- lib/symmetric_encryption/keystore/heroku.rb
|
58
57
|
- lib/symmetric_encryption/keystore/memory.rb
|
59
58
|
- lib/symmetric_encryption/railtie.rb
|
59
|
+
- lib/symmetric_encryption/railties/attr_encrypted.rb
|
60
|
+
- lib/symmetric_encryption/railties/mongoid_encrypted.rb
|
60
61
|
- lib/symmetric_encryption/railties/symmetric_encryption_validator.rb
|
61
62
|
- lib/symmetric_encryption/reader.rb
|
62
63
|
- lib/symmetric_encryption/rsa_key.rb
|
63
64
|
- lib/symmetric_encryption/symmetric_encryption.rb
|
64
65
|
- lib/symmetric_encryption/utils/aws.rb
|
66
|
+
- lib/symmetric_encryption/utils/files.rb
|
65
67
|
- lib/symmetric_encryption/utils/re_encrypt_files.rb
|
66
68
|
- lib/symmetric_encryption/version.rb
|
67
69
|
- lib/symmetric_encryption/writer.rb
|
@@ -1,110 +0,0 @@
|
|
1
|
-
module ActiveRecord #:nodoc:
|
2
|
-
class Base
|
3
|
-
# Transparently encrypt and decrypt values stored via ActiveRecord.
|
4
|
-
#
|
5
|
-
# Parameters:
|
6
|
-
# * Symbolic names of each method to create which has a corresponding
|
7
|
-
# method already defined in rails starting with: encrypted_
|
8
|
-
# * Followed by an optional hash:
|
9
|
-
# :random_iv [true|false]
|
10
|
-
# Whether the encrypted value should use a random IV every time the
|
11
|
-
# field is encrypted.
|
12
|
-
# It is recommended to set this to true where feasible. If the encrypted
|
13
|
-
# value could be used as part of a SQL where clause, or as part
|
14
|
-
# of any lookup, then it must be false.
|
15
|
-
# Setting random_iv to true will result in a different encrypted output for
|
16
|
-
# the same input string.
|
17
|
-
# Note: Only set to true if the field will never be used as part of
|
18
|
-
# the where clause in an SQL query.
|
19
|
-
# Note: When random_iv is true it will add a 8 byte header, plus the bytes
|
20
|
-
# to store the random IV in every returned encrypted string, prior to the
|
21
|
-
# encoding if any.
|
22
|
-
# Default: false
|
23
|
-
# Highly Recommended where feasible: true
|
24
|
-
#
|
25
|
-
# :type [Symbol]
|
26
|
-
# The type for this field, #see SymmetricEncryption::COERCION_TYPES
|
27
|
-
# Default: :string
|
28
|
-
#
|
29
|
-
# :compress [true|false]
|
30
|
-
# Whether to compress str before encryption
|
31
|
-
# Should only be used for large strings since compression overhead and
|
32
|
-
# the overhead of adding the 'magic' header may exceed any benefits of
|
33
|
-
# compression
|
34
|
-
# Note: Adds a 6 byte header prior to encoding, only if :random_iv is false
|
35
|
-
# Default: false
|
36
|
-
def self.attr_encrypted(*params)
|
37
|
-
# Ensure ActiveRecord has created all its methods first
|
38
|
-
# Ignore failures since the table may not yet actually exist
|
39
|
-
begin
|
40
|
-
define_attribute_methods
|
41
|
-
rescue StandardError
|
42
|
-
nil
|
43
|
-
end
|
44
|
-
|
45
|
-
options = params.last.is_a?(Hash) ? params.pop.dup : {}
|
46
|
-
|
47
|
-
params.each do |attribute|
|
48
|
-
SymmetricEncryption::Generator.generate_decrypted_accessors(self, attribute, "encrypted_#{attribute}", options)
|
49
|
-
encrypted_attributes[attribute.to_sym] = "encrypted_#{attribute}".to_sym
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Contains a hash of encrypted attributes with virtual attribute names as keys and real attribute
|
54
|
-
# names as values
|
55
|
-
#
|
56
|
-
# Example
|
57
|
-
#
|
58
|
-
# class User < ActiveRecord::Base
|
59
|
-
# attr_encrypted :email
|
60
|
-
# end
|
61
|
-
#
|
62
|
-
# User.encrypted_attributes => { email: encrypted_email }
|
63
|
-
def self.encrypted_attributes
|
64
|
-
@encrypted_attributes ||= superclass.respond_to?(:encrypted_attributes) ? superclass.encrypted_attributes.dup : {}
|
65
|
-
end
|
66
|
-
|
67
|
-
# Return the name of all encrypted virtual attributes as an Array of symbols
|
68
|
-
# Example: [:email, :password]
|
69
|
-
def self.encrypted_keys
|
70
|
-
@encrypted_keys ||= encrypted_attributes.keys
|
71
|
-
end
|
72
|
-
|
73
|
-
# Return the name of all encrypted columns as an Array of symbols
|
74
|
-
# Example: [:encrypted_email, :encrypted_password]
|
75
|
-
def self.encrypted_columns
|
76
|
-
@encrypted_columns ||= encrypted_attributes.values
|
77
|
-
end
|
78
|
-
|
79
|
-
# Returns whether an attribute has been configured to be encrypted
|
80
|
-
#
|
81
|
-
# Example
|
82
|
-
#
|
83
|
-
# class User < ActiveRecord::Base
|
84
|
-
# attr_accessor :name
|
85
|
-
# attr_encrypted :email
|
86
|
-
# end
|
87
|
-
#
|
88
|
-
# User.encrypted_attribute?(:name) # false
|
89
|
-
# User.encrypted_attribute?(:email) # true
|
90
|
-
def self.encrypted_attribute?(attribute)
|
91
|
-
encrypted_keys.include?(attribute)
|
92
|
-
end
|
93
|
-
|
94
|
-
# Returns whether the attribute is the database column to hold the
|
95
|
-
# encrypted data for a matching encrypted attribute
|
96
|
-
#
|
97
|
-
# Example
|
98
|
-
#
|
99
|
-
# class User < ActiveRecord::Base
|
100
|
-
# attr_accessor :name
|
101
|
-
# attr_encrypted :email
|
102
|
-
# end
|
103
|
-
#
|
104
|
-
# User.encrypted_column?(:encrypted_name) # false
|
105
|
-
# User.encrypted_column?(:encrypted_email) # true
|
106
|
-
def self.encrypted_column?(attribute)
|
107
|
-
encrypted_columns.include?(attribute)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# DEPRECATED !!!
|
3
|
-
#
|
4
|
-
module MongoMapper
|
5
|
-
module Plugins
|
6
|
-
module EncryptedKey
|
7
|
-
extend ActiveSupport::Concern
|
8
|
-
|
9
|
-
COERCION_MAP = {
|
10
|
-
String => :string,
|
11
|
-
Integer => :integer,
|
12
|
-
Float => :float,
|
13
|
-
BigDecimal => :decimal,
|
14
|
-
DateTime => :datetime,
|
15
|
-
Time => :time,
|
16
|
-
Date => :date,
|
17
|
-
Boolean => :boolean,
|
18
|
-
Hash => :json
|
19
|
-
}.freeze
|
20
|
-
|
21
|
-
module ClassMethods
|
22
|
-
def encrypted_key(key_name, type, full_options = {})
|
23
|
-
full_options = full_options.is_a?(Hash) ? full_options.dup : {}
|
24
|
-
options = full_options.delete(:encrypted) || {}
|
25
|
-
# Support overriding the name of the decrypted attribute
|
26
|
-
encrypted_key_name = options.delete(:encrypt_as) || "encrypted_#{key_name}"
|
27
|
-
options[:type] = COERCION_MAP[type] unless %i[yaml json].include?(options[:type])
|
28
|
-
|
29
|
-
raise(ArgumentError, "Invalid type: #{type.inspect}. Valid types: #{COERCION_MAP.keys.join(',')}") unless options[:type]
|
30
|
-
|
31
|
-
SymmetricEncryption::Generator.generate_decrypted_accessors(self, key_name, encrypted_key_name, options)
|
32
|
-
|
33
|
-
key(encrypted_key_name, String, full_options)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
MongoMapper::Document.plugin(MongoMapper::Plugins::EncryptedKey)
|
41
|
-
MongoMapper::EmbeddedDocument.plugin(MongoMapper::Plugins::EncryptedKey)
|