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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -7
  3. data/Rakefile +9 -9
  4. data/bin/symmetric-encryption +1 -1
  5. data/lib/symmetric-encryption.rb +1 -1
  6. data/lib/symmetric_encryption/active_record/attr_encrypted.rb +129 -0
  7. data/lib/symmetric_encryption/active_record/encrypted_attribute.rb +37 -0
  8. data/lib/symmetric_encryption/cipher.rb +20 -14
  9. data/lib/symmetric_encryption/cli.rb +76 -58
  10. data/lib/symmetric_encryption/coerce.rb +3 -3
  11. data/lib/symmetric_encryption/config.rb +37 -28
  12. data/lib/symmetric_encryption/core.rb +35 -0
  13. data/lib/symmetric_encryption/encoder.rb +26 -8
  14. data/lib/symmetric_encryption/generator.rb +7 -3
  15. data/lib/symmetric_encryption/header.rb +24 -24
  16. data/lib/symmetric_encryption/key.rb +1 -1
  17. data/lib/symmetric_encryption/keystore/aws.rb +14 -32
  18. data/lib/symmetric_encryption/keystore/environment.rb +5 -5
  19. data/lib/symmetric_encryption/keystore/file.rb +34 -17
  20. data/lib/symmetric_encryption/keystore/gcp.rb +90 -0
  21. data/lib/symmetric_encryption/keystore/heroku.rb +1 -1
  22. data/lib/symmetric_encryption/keystore/memory.rb +3 -3
  23. data/lib/symmetric_encryption/keystore.rb +23 -22
  24. data/lib/symmetric_encryption/railtie.rb +14 -13
  25. data/lib/symmetric_encryption/{extensions/mongoid/encrypted.rb → railties/mongoid_encrypted.rb} +5 -4
  26. data/lib/symmetric_encryption/railties/symmetric_encryption_validator.rb +1 -1
  27. data/lib/symmetric_encryption/reader.rb +13 -13
  28. data/lib/symmetric_encryption/rsa_key.rb +1 -1
  29. data/lib/symmetric_encryption/symmetric_encryption.rb +56 -36
  30. data/lib/symmetric_encryption/utils/aws.rb +8 -10
  31. data/lib/symmetric_encryption/utils/files.rb +45 -0
  32. data/lib/symmetric_encryption/utils/re_encrypt_files.rb +11 -11
  33. data/lib/symmetric_encryption/version.rb +1 -1
  34. data/lib/symmetric_encryption/writer.rb +20 -13
  35. data/lib/symmetric_encryption.rb +19 -49
  36. metadata +14 -13
  37. data/lib/symmetric_encryption/extensions/active_record/base.rb +0 -110
  38. 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: ada00eaa90ed1edb19723bfc1e0e34f3e6f8be5f060b6c2a79eaf046483f77b0
4
- data.tar.gz: 0bf3ae34653d9598fc0a0be1058aa366f1e401b053bad92995ef08f00dc1fcbc
3
+ metadata.gz: cc6728652282c5b73acde1b45427e7dbce0092ceecc197a052f5364b83c53e28
4
+ data.tar.gz: dad8b275ffd46adf20d2b3f51c7673b79e1cc359750626d4207f203a207c14f0
5
5
  SHA512:
6
- metadata.gz: 8299b773b5fbe49452187aeaa746343e9aff7c49a86f226142ae105a1aa9d1ad29401aa44297d19c6ae79161c2e8f0a9ad4f159e36c7191df5b36bb704367ef1
7
- data.tar.gz: f9a37ecd2aa9c95eeb543e08be08de1a859bb1e0745241d9e983534e9b3df334bcc1486f6a46430ab2bfeaa5286a5902bffacaced9c9c0d53526cfe16d29b357
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://img.shields.io/travis/rocketjob/symmetric-encryption.svg) ![](https://img.shields.io/gem/dt/symmetric-encryption.svg) ![](https://img.shields.io/badge/status-production%20ready-blue.svg)
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
- * http://github.com/rocketjob/symmetric-encryption
4
+ * https://encryption.rocketjob.io/
5
5
 
6
- Transparently encrypt ActiveRecord, Mongoid, and MongoMapper attributes. Encrypt passwords in configuration files. Encrypt entire files at rest.
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](http://rocketjob.github.io/symmetric-encryption)
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/rocketjob/symmetric-encryption/graphs/contributors)
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 'rubygems'
3
- require 'bundler/setup'
2
+ require "rubygems"
3
+ require "bundler/setup"
4
4
 
5
- require 'rake/testtask'
6
- require_relative 'lib/symmetric_encryption/version'
5
+ require "rake/testtask"
6
+ require_relative "lib/symmetric_encryption/version"
7
7
 
8
8
  task :gem do
9
- system 'gem build symmetric-encryption.gemspec'
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 'git push --tags'
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 = 'test/**/*_test.rb'
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['APPRAISAL_INITIALIZED'] && !ENV['TRAVIS']
27
- require 'appraisal'
26
+ if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
27
+ require "appraisal"
28
28
  task default: :appraisal
29
29
  else
30
30
  task default: :test
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'symmetric_encryption'
3
+ require "symmetric_encryption"
4
4
 
5
5
  SymmetricEncryption::CLI.run!(ARGV)
@@ -1 +1 @@
1
- require 'symmetric_encryption'
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 'openssl'
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: 'aes-256-cbc',
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 [Fixnum]
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: 'aes-256-cbc',
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
- raise(ArgumentError, "Cipher version has a valid range of 0 to 255. #{@version} is too high, or negative") if (@version > 255) || @version.negative?
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 encypted value should use a random IV every time 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: false, compress: false, header: always_add_header)
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
- decrypted.force_encoding(SymmetricEncryption::BINARY_ENCODING) unless decrypted.force_encoding(SymmetricEncryption::UTF8_ENCODING).valid_encoding?
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: false, compress: false, header: always_add_header)
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 = header.key || @key
320
- if (iv = header.iv || @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('=').strip
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