symmetric-encryption 4.1.2 → 4.5.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/active_record/attr_encrypted.rb +129 -0
- data/lib/symmetric_encryption/active_record/encrypted_attribute.rb +37 -0
- data/lib/symmetric_encryption/cipher.rb +20 -14
- data/lib/symmetric_encryption/cli.rb +76 -58
- data/lib/symmetric_encryption/coerce.rb +3 -3
- data/lib/symmetric_encryption/config.rb +37 -28
- data/lib/symmetric_encryption/core.rb +35 -0
- data/lib/symmetric_encryption/encoder.rb +26 -8
- data/lib/symmetric_encryption/generator.rb +7 -3
- data/lib/symmetric_encryption/header.rb +24 -24
- data/lib/symmetric_encryption/key.rb +1 -1
- data/lib/symmetric_encryption/keystore/aws.rb +14 -32
- data/lib/symmetric_encryption/keystore/environment.rb +5 -5
- data/lib/symmetric_encryption/keystore/file.rb +34 -17
- data/lib/symmetric_encryption/keystore/gcp.rb +90 -0
- data/lib/symmetric_encryption/keystore/heroku.rb +1 -1
- data/lib/symmetric_encryption/keystore/memory.rb +3 -3
- data/lib/symmetric_encryption/keystore.rb +23 -22
- data/lib/symmetric_encryption/railtie.rb +14 -13
- data/lib/symmetric_encryption/{extensions/mongoid/encrypted.rb → railties/mongoid_encrypted.rb} +5 -4
- data/lib/symmetric_encryption/railties/symmetric_encryption_validator.rb +1 -1
- data/lib/symmetric_encryption/reader.rb +13 -13
- data/lib/symmetric_encryption/rsa_key.rb +1 -1
- data/lib/symmetric_encryption/symmetric_encryption.rb +56 -36
- data/lib/symmetric_encryption/utils/aws.rb +8 -10
- data/lib/symmetric_encryption/utils/files.rb +45 -0
- data/lib/symmetric_encryption/utils/re_encrypt_files.rb +11 -11
- data/lib/symmetric_encryption/version.rb +1 -1
- data/lib/symmetric_encryption/writer.rb +20 -13
- data/lib/symmetric_encryption.rb +19 -49
- metadata +14 -13
- data/lib/symmetric_encryption/extensions/active_record/base.rb +0 -110
- data/lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb +0 -41
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cc6728652282c5b73acde1b45427e7dbce0092ceecc197a052f5364b83c53e28
|
|
4
|
+
data.tar.gz: dad8b275ffd46adf20d2b3f51c7673b79e1cc359750626d4207f203a207c14f0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: efd513c2c0b22b5252583a3d8207d89f907b695f6edf6c75d0f7a2c9177e8d32d5566d7228f36b7039318d4e72380f4cee4090048cf40f7bf50a492bf525fcc5
|
|
7
|
+
data.tar.gz: bac21c5250923fd85134cc3a59e220ec2c87c85e4dc7f4efef280110c01852dd202256bcad5671d96dd7c7424c531b2bd237f0f9f85d5ac22bce24415ab6fcde
|
data/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Symmetric Encryption
|
|
2
|
-
 ](https://rubygems.org/gems/symmetric-encryption) [](https://github.com/reidmorrison/symmetric-encryption/actions?query=workflow%3Abuild) [](https://rubygems.org/gems/symmetric-encryption) [](http://opensource.org/licenses/Apache-2.0) 
|
|
3
3
|
|
|
4
|
-
*
|
|
4
|
+
* https://encryption.rocketjob.io/
|
|
5
5
|
|
|
6
|
-
Transparently encrypt ActiveRecord,
|
|
6
|
+
Transparently encrypt ActiveRecord, and Mongoid attributes. Encrypt passwords in configuration files. Encrypt entire files at rest.
|
|
7
7
|
|
|
8
8
|
## Introduction
|
|
9
9
|
|
|
@@ -19,9 +19,7 @@ expose all the encryption algorithms supported by OpenSSL.
|
|
|
19
19
|
|
|
20
20
|
## Documentation
|
|
21
21
|
|
|
22
|
-
[Symmetric Encryption Guide](
|
|
23
|
-
|
|
24
|
-
[Reference Documentation](http://www.rubydoc.info/gems/symmetric-encryption/)
|
|
22
|
+
[Symmetric Encryption Guide](https://encryption.rocketjob.io/)
|
|
25
23
|
|
|
26
24
|
## Rocket Job
|
|
27
25
|
|
|
@@ -168,7 +166,7 @@ may have backward compatibility issues:
|
|
|
168
166
|
|
|
169
167
|
[Reid Morrison](https://github.com/reidmorrison)
|
|
170
168
|
|
|
171
|
-
[Contributors](https://github.com/
|
|
169
|
+
[Contributors](https://github.com/reidmorrison/symmetric-encryption/graphs/contributors)
|
|
172
170
|
|
|
173
171
|
## Versioning
|
|
174
172
|
|
data/Rakefile
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
# Setup bundler to avoid having to run bundle exec all the time.
|
|
2
|
-
require
|
|
3
|
-
require
|
|
2
|
+
require "rubygems"
|
|
3
|
+
require "bundler/setup"
|
|
4
4
|
|
|
5
|
-
require
|
|
6
|
-
require_relative
|
|
5
|
+
require "rake/testtask"
|
|
6
|
+
require_relative "lib/symmetric_encryption/version"
|
|
7
7
|
|
|
8
8
|
task :gem do
|
|
9
|
-
system
|
|
9
|
+
system "gem build symmetric-encryption.gemspec"
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
task publish: :gem do
|
|
13
13
|
system "git tag -a v#{SymmetricEncryption::VERSION} -m 'Tagging #{SymmetricEncryption::VERSION}'"
|
|
14
|
-
system
|
|
14
|
+
system "git push --tags"
|
|
15
15
|
system "gem push symmetric-encryption-#{SymmetricEncryption::VERSION}.gem"
|
|
16
16
|
system "rm symmetric-encryption-#{SymmetricEncryption::VERSION}.gem"
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
Rake::TestTask.new(:test) do |t|
|
|
20
|
-
t.pattern =
|
|
20
|
+
t.pattern = "test/**/*_test.rb"
|
|
21
21
|
t.verbose = true
|
|
22
22
|
t.warning = false
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
# By default run tests against all appraisals
|
|
26
|
-
if !ENV[
|
|
27
|
-
require
|
|
26
|
+
if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
|
|
27
|
+
require "appraisal"
|
|
28
28
|
task default: :appraisal
|
|
29
29
|
else
|
|
30
30
|
task default: :test
|
data/bin/symmetric-encryption
CHANGED
data/lib/symmetric-encryption.rb
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "symmetric_encryption"
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
module SymmetricEncryption
|
|
2
|
+
module ActiveRecord
|
|
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(*attributes, random_iv: nil, type: :string, compress: false)
|
|
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
|
+
random_iv = true if random_iv.nil? && SymmetricEncryption.randomize_iv?
|
|
52
|
+
|
|
53
|
+
if random_iv.nil?
|
|
54
|
+
warn("attr_encrypted() no longer allows a default value for option `random_iv`. Add `random_iv: false` if it is required.")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
attributes.each do |attribute|
|
|
58
|
+
SymmetricEncryption::Generator.generate_decrypted_accessors(
|
|
59
|
+
self,
|
|
60
|
+
attribute,
|
|
61
|
+
"encrypted_#{attribute}",
|
|
62
|
+
random_iv: random_iv,
|
|
63
|
+
type: type,
|
|
64
|
+
compress: compress
|
|
65
|
+
)
|
|
66
|
+
encrypted_attributes[attribute.to_sym] = "encrypted_#{attribute}".to_sym
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Contains a hash of encrypted attributes with virtual attribute names as keys and real attribute
|
|
71
|
+
# names as values
|
|
72
|
+
#
|
|
73
|
+
# Example
|
|
74
|
+
#
|
|
75
|
+
# class User < ActiveRecord::Base
|
|
76
|
+
# attr_encrypted :email
|
|
77
|
+
# end
|
|
78
|
+
#
|
|
79
|
+
# User.encrypted_attributes => { email: encrypted_email }
|
|
80
|
+
def encrypted_attributes
|
|
81
|
+
@encrypted_attributes ||= superclass.respond_to?(:encrypted_attributes) ? superclass.encrypted_attributes.dup : {}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Return the name of all encrypted virtual attributes as an Array of symbols
|
|
85
|
+
# Example: [:email, :password]
|
|
86
|
+
def encrypted_keys
|
|
87
|
+
@encrypted_keys ||= encrypted_attributes.keys
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Return the name of all encrypted columns as an Array of symbols
|
|
91
|
+
# Example: [:encrypted_email, :encrypted_password]
|
|
92
|
+
def encrypted_columns
|
|
93
|
+
@encrypted_columns ||= encrypted_attributes.values
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Returns whether an attribute has been configured to be encrypted
|
|
97
|
+
#
|
|
98
|
+
# Example
|
|
99
|
+
#
|
|
100
|
+
# class User < ActiveRecord::Base
|
|
101
|
+
# attr_accessor :name
|
|
102
|
+
# attr_encrypted :email
|
|
103
|
+
# end
|
|
104
|
+
#
|
|
105
|
+
# User.encrypted_attribute?(:name) # false
|
|
106
|
+
# User.encrypted_attribute?(:email) # true
|
|
107
|
+
def encrypted_attribute?(attribute)
|
|
108
|
+
encrypted_keys.include?(attribute)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Returns whether the attribute is the database column to hold the
|
|
112
|
+
# encrypted data for a matching encrypted attribute
|
|
113
|
+
#
|
|
114
|
+
# Example
|
|
115
|
+
#
|
|
116
|
+
# class User < ActiveRecord::Base
|
|
117
|
+
# attr_accessor :name
|
|
118
|
+
# attr_encrypted :email
|
|
119
|
+
# end
|
|
120
|
+
#
|
|
121
|
+
# User.encrypted_column?(:encrypted_name) # false
|
|
122
|
+
# User.encrypted_column?(:encrypted_email) # true
|
|
123
|
+
def encrypted_column?(attribute)
|
|
124
|
+
encrypted_columns.include?(attribute)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module SymmetricEncryption
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
class EncryptedAttribute < ::ActiveModel::Type::String
|
|
4
|
+
def initialize(random_iv: true, compress: false, type: :string)
|
|
5
|
+
@random_iv = random_iv
|
|
6
|
+
@compress = compress
|
|
7
|
+
@encrypted_type = type
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def deserialize(value)
|
|
11
|
+
return if value.nil?
|
|
12
|
+
|
|
13
|
+
SymmetricEncryption.decrypt(value, type: encrypted_type)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def serialize(value)
|
|
17
|
+
return if value.nil?
|
|
18
|
+
|
|
19
|
+
SymmetricEncryption.encrypt(
|
|
20
|
+
value,
|
|
21
|
+
type: encrypted_type,
|
|
22
|
+
compress: compress,
|
|
23
|
+
random_iv: random_iv
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
# Symmetric Encryption uses coercible gem to handle casting
|
|
30
|
+
def cast_value(value)
|
|
31
|
+
value
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
attr_reader :random_iv, :compress, :encrypted_type
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "openssl"
|
|
2
2
|
module SymmetricEncryption
|
|
3
3
|
# Hold all information related to encryption keys
|
|
4
4
|
# as well as encrypt and decrypt data using those keys.
|
|
@@ -12,7 +12,7 @@ module SymmetricEncryption
|
|
|
12
12
|
attr_writer :key
|
|
13
13
|
|
|
14
14
|
# Returns [Cipher] from a cipher config instance.
|
|
15
|
-
def self.from_config(cipher_name:
|
|
15
|
+
def self.from_config(cipher_name: "aes-256-cbc",
|
|
16
16
|
version: 0,
|
|
17
17
|
always_add_header: true,
|
|
18
18
|
encoding: :base64strict,
|
|
@@ -50,6 +50,8 @@ module SymmetricEncryption
|
|
|
50
50
|
# This is the recommended format since newlines in the values to
|
|
51
51
|
# SQL queries are cumbersome. Also the newline reformatting is unnecessary
|
|
52
52
|
# It is not the default for backward compatibility
|
|
53
|
+
# :base64urlsafe
|
|
54
|
+
# Same as base64strict except that base64urlsafe uses '-' instead of '+' and '_' instead of '/'.
|
|
53
55
|
# :base64
|
|
54
56
|
# Return as a base64 encoded string
|
|
55
57
|
# :base16
|
|
@@ -58,7 +60,7 @@ module SymmetricEncryption
|
|
|
58
60
|
# Return as raw binary data string. Note: String can contain embedded nulls
|
|
59
61
|
# Default: :base64strict
|
|
60
62
|
#
|
|
61
|
-
# version [
|
|
63
|
+
# version [Integer]
|
|
62
64
|
# Optional. The version number of this encryption key
|
|
63
65
|
# Used by SymmetricEncryption to select the correct key when decrypting data
|
|
64
66
|
# Valid Range: 0..255
|
|
@@ -72,7 +74,7 @@ module SymmetricEncryption
|
|
|
72
74
|
# Default: true
|
|
73
75
|
def initialize(key:,
|
|
74
76
|
iv: nil,
|
|
75
|
-
cipher_name:
|
|
77
|
+
cipher_name: "aes-256-cbc",
|
|
76
78
|
version: 0,
|
|
77
79
|
always_add_header: true,
|
|
78
80
|
encoding: :base64strict)
|
|
@@ -84,7 +86,9 @@ module SymmetricEncryption
|
|
|
84
86
|
@version = version.to_i
|
|
85
87
|
@always_add_header = always_add_header
|
|
86
88
|
|
|
87
|
-
|
|
89
|
+
return unless (@version > 255) || @version.negative?
|
|
90
|
+
|
|
91
|
+
raise(ArgumentError, "Cipher version has a valid range of 0 to 255. #{@version} is too high, or negative")
|
|
88
92
|
end
|
|
89
93
|
|
|
90
94
|
# Change the encoding
|
|
@@ -112,7 +116,7 @@ module SymmetricEncryption
|
|
|
112
116
|
# to convert it to a string
|
|
113
117
|
#
|
|
114
118
|
# random_iv [true|false]
|
|
115
|
-
# Whether the
|
|
119
|
+
# Whether the encrypted value should use a random IV every time the
|
|
116
120
|
# field is encrypted.
|
|
117
121
|
# Notes:
|
|
118
122
|
# * Setting random_iv to true will result in a different encrypted output for
|
|
@@ -131,7 +135,7 @@ module SymmetricEncryption
|
|
|
131
135
|
# * Should only be used for large strings since compression overhead and
|
|
132
136
|
# the overhead of adding the encryption header may exceed any benefits of
|
|
133
137
|
# compression
|
|
134
|
-
def encrypt(str, random_iv:
|
|
138
|
+
def encrypt(str, random_iv: SymmetricEncryption.randomize_iv?, compress: false, header: always_add_header)
|
|
135
139
|
return if str.nil?
|
|
136
140
|
|
|
137
141
|
str = str.to_s
|
|
@@ -167,7 +171,9 @@ module SymmetricEncryption
|
|
|
167
171
|
decrypted = binary_decrypt(decoded)
|
|
168
172
|
|
|
169
173
|
# Try to force result to UTF-8 encoding, but if it is not valid, force it back to Binary
|
|
170
|
-
|
|
174
|
+
unless decrypted.force_encoding(SymmetricEncryption::UTF8_ENCODING).valid_encoding?
|
|
175
|
+
decrypted.force_encoding(SymmetricEncryption::BINARY_ENCODING)
|
|
176
|
+
end
|
|
171
177
|
|
|
172
178
|
decrypted
|
|
173
179
|
end
|
|
@@ -180,7 +186,7 @@ module SymmetricEncryption
|
|
|
180
186
|
#
|
|
181
187
|
# Returned string is UTF8 encoded except for encoding :none
|
|
182
188
|
def encode(binary_string)
|
|
183
|
-
return binary_string if binary_string.nil? || (binary_string ==
|
|
189
|
+
return binary_string if binary_string.nil? || (binary_string == "")
|
|
184
190
|
|
|
185
191
|
encoder.encode(binary_string)
|
|
186
192
|
end
|
|
@@ -190,7 +196,7 @@ module SymmetricEncryption
|
|
|
190
196
|
#
|
|
191
197
|
# Returned string is Binary encoded
|
|
192
198
|
def decode(encoded_string)
|
|
193
|
-
return encoded_string if encoded_string.nil? || (encoded_string ==
|
|
199
|
+
return encoded_string if encoded_string.nil? || (encoded_string == "")
|
|
194
200
|
|
|
195
201
|
encoder.decode(encoded_string)
|
|
196
202
|
end
|
|
@@ -246,7 +252,7 @@ module SymmetricEncryption
|
|
|
246
252
|
# Default: `always_add_header`
|
|
247
253
|
#
|
|
248
254
|
# See #encrypt to encrypt and encode the result as a string.
|
|
249
|
-
def binary_encrypt(str, random_iv:
|
|
255
|
+
def binary_encrypt(str, random_iv: SymmetricEncryption.randomize_iv?, compress: false, header: always_add_header)
|
|
250
256
|
return if str.nil?
|
|
251
257
|
|
|
252
258
|
string = str.to_s
|
|
@@ -316,8 +322,8 @@ module SymmetricEncryption
|
|
|
316
322
|
|
|
317
323
|
openssl_cipher = ::OpenSSL::Cipher.new(header.cipher_name || cipher_name)
|
|
318
324
|
openssl_cipher.decrypt
|
|
319
|
-
openssl_cipher.key
|
|
320
|
-
if (iv
|
|
325
|
+
openssl_cipher.key = header.key || @key
|
|
326
|
+
if (iv = header.iv || @iv)
|
|
321
327
|
openssl_cipher.iv = iv
|
|
322
328
|
end
|
|
323
329
|
result = openssl_cipher.update(data)
|
|
@@ -327,7 +333,7 @@ module SymmetricEncryption
|
|
|
327
333
|
|
|
328
334
|
# Returns the magic header after applying the encoding in this cipher
|
|
329
335
|
def encoded_magic_header
|
|
330
|
-
@encoded_magic_header ||= encoder.encode(SymmetricEncryption::Header::MAGIC_HEADER).delete(
|
|
336
|
+
@encoded_magic_header ||= encoder.encode(SymmetricEncryption::Header::MAGIC_HEADER).delete("=").strip
|
|
331
337
|
end
|
|
332
338
|
|
|
333
339
|
# Returns [String] object represented as a string, filtering out the key
|