symmetric-encryption 4.1.4 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|