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.
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