symmetric-encryption 4.1.2 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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://img.shields.io/gem/v/symmetric-encryption.svg) ![](https://
|
2
|
+
[![Gem Version](https://img.shields.io/gem/v/symmetric-encryption.svg)](https://rubygems.org/gems/symmetric-encryption) [![Build Status](https://github.com/reidmorrison/symmetric-encryption/workflows/build/badge.svg)](https://github.com/reidmorrison/symmetric-encryption/actions?query=workflow%3Abuild) [![Downloads](https://img.shields.io/gem/dt/symmetric-encryption.svg)](https://rubygems.org/gems/symmetric-encryption) [![License](https://img.shields.io/badge/license-Apache%202.0-brightgreen.svg)](http://opensource.org/licenses/Apache-2.0) ![](https://img.shields.io/badge/status-Production%20Ready-blue.svg)
|
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
|