symmetric-encryption 3.2 → 3.3
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/LICENSE.txt +1 -1
- data/README.md +67 -11
- data/lib/symmetric_encryption.rb +7 -9
- data/lib/symmetric_encryption/extensions/active_record/base.rb +29 -11
- data/lib/symmetric_encryption/mongoid.rb +29 -7
- data/lib/symmetric_encryption/railties/symmetric_encryption.rake +5 -1
- data/lib/symmetric_encryption/symmetric_encryption.rb +94 -7
- data/lib/symmetric_encryption/version.rb +1 -1
- data/test/attr_encrypted_test.rb +315 -17
- data/test/cipher_test.rb +1 -10
- data/test/field_encrypted_test.rb +306 -19
- data/test/reader_test.rb +1 -10
- data/test/symmetric_encryption_test.rb +113 -10
- data/test/test_db.sqlite3 +0 -0
- data/test/test_helper.rb +19 -0
- data/test/writer_test.rb +1 -10
- metadata +28 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37b9132a3f23db50841774bccd1d0ea48db0a325
|
4
|
+
data.tar.gz: 66808f69ba790855acac29dcd2ca59b59ec5a611
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33ba910830b6f113ccefe74d029e2ec5d68f3ea15234bae0234c7de335d9d9e29f13edbc248398f6d766e02277ee22535f8d274cae10b48bcc9b8f388de81af5
|
7
|
+
data.tar.gz: c0dbfec5a29239451d18cd4ec8eef68f891100267a1b670e4b9224861b3cf9321ae7f525fc08ca7927aca260c72ec6696da40cca57af243d343bf288b28c40be
|
data/LICENSE.txt
CHANGED
@@ -186,7 +186,7 @@
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
187
187
|
identification within third-party archives.
|
188
188
|
|
189
|
-
Copyright 2012
|
189
|
+
Copyright 2012, 2013, 2014 Reid Morrison
|
190
190
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
192
192
|
you may not use this file except in compliance with the License.
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
symmetric-encryption
|
2
2
|
====================
|
3
3
|
|
4
|
-
* http://github.com/
|
4
|
+
* http://github.com/reidmorrison/symmetric-encryption
|
5
5
|
|
6
6
|
## Introduction
|
7
7
|
|
@@ -163,18 +163,40 @@ class User < ActiveRecord::Base
|
|
163
163
|
# Requires table users to have a column called encrypted_bank_account_number
|
164
164
|
attr_encrypted :bank_account_number
|
165
165
|
|
166
|
-
# Requires table
|
166
|
+
# Requires users table to have a column called encrypted_social_security_number
|
167
|
+
#
|
168
|
+
# Note: Encrypting the same value twice will result in the _same_ encrypted value
|
169
|
+
# when :random_iv => false, or is not specified
|
167
170
|
attr_encrypted :social_security_number
|
168
171
|
|
172
|
+
# By specifying the type as :integer the value will be returned as an integer and
|
173
|
+
# can be set as an integer, even though it is stored in the database as an
|
174
|
+
# encrypted string
|
175
|
+
#
|
176
|
+
# Requires users table to have a column called encrypted_age of type string
|
177
|
+
attr_encrypted :age, :type => :integer
|
178
|
+
|
169
179
|
# Since string and long_string are not used in the where clause of any SQL
|
170
180
|
# queries it is better to ensure that the encrypted value is always different
|
171
181
|
# by encrypting every value with a random Initialization Vector.
|
182
|
+
#
|
183
|
+
# Note: Encrypting the same value twice will result in different encrypted
|
184
|
+
# values when :random_iv => true
|
172
185
|
attr_encrypted :string, :random_iv => true
|
173
186
|
|
174
187
|
# Long encrypted strings can also be compressed prior to encryption to save
|
175
188
|
# disk space
|
176
189
|
attr_encrypted :long_string, :random_iv => true, :compress => true
|
177
190
|
|
191
|
+
# By specifying the type as :json the value will be serialized to JSON
|
192
|
+
# before encryption and deserialized from JSON after decryption.
|
193
|
+
#
|
194
|
+
# It is sometimes useful to use compression on large fields, so we can enable
|
195
|
+
# compression before the string is encrypted
|
196
|
+
#
|
197
|
+
# Requires users table to have a column called encrypted_values of type string
|
198
|
+
attr_encrypted :values, :type => :json, :compress => true
|
199
|
+
|
178
200
|
validates :encrypted_bank_account_number, :symmetric_encryption => true
|
179
201
|
validates :encrypted_social_security_number, :symmetric_encryption => true
|
180
202
|
end
|
@@ -191,6 +213,19 @@ user.save!
|
|
191
213
|
User.create(:bank_account_number => '12345')
|
192
214
|
```
|
193
215
|
|
216
|
+
Several types are supported for ActiveRecord models when encrypting or decrypting data.
|
217
|
+
Each type maps to the built-in Ruby types as follows:
|
218
|
+
|
219
|
+
- :string => String
|
220
|
+
- :integer => Integer
|
221
|
+
- :float => Float
|
222
|
+
- :decimal => BigDecimal
|
223
|
+
- :datetime => DateTime
|
224
|
+
- :time => Time
|
225
|
+
- :date => Date
|
226
|
+
- :json => Uses JSON serialization, useful for hashes and arrays
|
227
|
+
- :yaml => Uses YAML serialization, useful for hashes and arrays
|
228
|
+
|
194
229
|
### Mongoid Example
|
195
230
|
|
196
231
|
To encrypt a field in a Mongoid document, just add ":encrypted => true" at the end
|
@@ -205,6 +240,13 @@ class User
|
|
205
240
|
field :encrypted_bank_account_number, :type => String, :encrypted => true
|
206
241
|
field :encrypted_social_security_number, :type => String, :encrypted => true
|
207
242
|
field :encrypted_life_history, :type => String, :encrypted => {:compress => true, :random_iv => true}
|
243
|
+
|
244
|
+
# Encrypted fields are _always_ stored in Mongo as a String
|
245
|
+
# To get the result back as an Integer, Symmetric Encryption can do the
|
246
|
+
# necessary conversions by specifying the internal type as an option
|
247
|
+
# to :encrypted
|
248
|
+
# #see SymmetricEncryption::COERCION_TYPES for full list of types
|
249
|
+
field :encrypted_age, :type => String, :encrypted => {:type => :integer}
|
208
250
|
end
|
209
251
|
|
210
252
|
# Create a new user document
|
@@ -217,6 +259,8 @@ user = User.where(:encrypted_bank_account_number => SymmetricEncryption.encrypt(
|
|
217
259
|
puts user.bank_account_number
|
218
260
|
```
|
219
261
|
|
262
|
+
Note: At this time Symmetric Encryption only supports Mongoid fields with a type of String
|
263
|
+
|
220
264
|
### Validation Example
|
221
265
|
|
222
266
|
```ruby
|
@@ -327,6 +371,13 @@ Encrypt a known value, such as a password:
|
|
327
371
|
Note: Passwords must be encrypted in the environment in which they will be used.
|
328
372
|
Since each environment should have its own symmetric encryption keys
|
329
373
|
|
374
|
+
Note: To use the rake task 'symmetric_encryption:encrypt' the gem 'highline'
|
375
|
+
must first be installed by adding to bundler or installing directly:
|
376
|
+
|
377
|
+
```ruby
|
378
|
+
gem install 'highline'
|
379
|
+
```
|
380
|
+
|
330
381
|
Encrypt a file
|
331
382
|
|
332
383
|
INFILE="Gemfile.lock" OUTFILE="Gemfile.lock.encrypted" rake symmetric_encryption:encrypt_file
|
@@ -614,9 +665,9 @@ production:
|
|
614
665
|
Meta
|
615
666
|
----
|
616
667
|
|
617
|
-
* Code: `git clone git://github.com/
|
618
|
-
* Home: <https://github.com/
|
619
|
-
* Issues: <http://github.com/
|
668
|
+
* Code: `git clone git://github.com/reidmorrison/symmetric-encryption.git`
|
669
|
+
* Home: <https://github.com/reidmorrison/symmetric-encryption>
|
670
|
+
* Issues: <http://github.com/reidmorrison/symmetric-encryption/issues>
|
620
671
|
* Gems: <http://rubygems.org/gems/symmetric-encryption>
|
621
672
|
|
622
673
|
This project uses [Semantic Versioning](http://semver.org/).
|
@@ -624,12 +675,17 @@ This project uses [Semantic Versioning](http://semver.org/).
|
|
624
675
|
Authors
|
625
676
|
-------
|
626
677
|
|
627
|
-
Reid Morrison
|
678
|
+
[Reid Morrison](https://github.com/reidmorrison)
|
679
|
+
|
680
|
+
Contributors
|
681
|
+
------------
|
682
|
+
|
683
|
+
[M. Scott Ford](https://github.com/mscottford)
|
628
684
|
|
629
685
|
License
|
630
686
|
-------
|
631
687
|
|
632
|
-
Copyright 2012
|
688
|
+
Copyright 2012, 2013, 2014 Reid Morrison
|
633
689
|
|
634
690
|
Licensed under the Apache License, Version 2.0 (the "License");
|
635
691
|
you may not use this file except in compliance with the License.
|
@@ -643,9 +699,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
643
699
|
See the License for the specific language governing permissions and
|
644
700
|
limitations under the License.
|
645
701
|
|
646
|
-
|
702
|
+
Disclaimer
|
647
703
|
----------
|
648
704
|
|
649
|
-
Although this library has assisted
|
650
|
-
way guarantees that PCI Compliance will be
|
651
|
-
|
705
|
+
Although this library has assisted in meeting PCI Compliance and has passed
|
706
|
+
previous PCI audits, it in no way guarantees that PCI Compliance will be
|
707
|
+
achieved by anyone using this library.
|
data/lib/symmetric_encryption.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
+
# Used for compression
|
1
2
|
require 'zlib'
|
3
|
+
# Used to coerce data types between string and their actual types
|
4
|
+
require 'coercible'
|
5
|
+
|
2
6
|
require 'symmetric_encryption/version'
|
3
7
|
require 'symmetric_encryption/cipher'
|
4
8
|
require 'symmetric_encryption/symmetric_encryption'
|
@@ -10,13 +14,7 @@ end
|
|
10
14
|
if defined?(Rails)
|
11
15
|
require 'symmetric_encryption/railtie'
|
12
16
|
end
|
13
|
-
# attr_encrypted and Encrypted validator
|
14
|
-
if defined?(ActiveRecord::Base)
|
15
|
-
require 'symmetric_encryption/extensions/active_record/base'
|
16
|
-
require 'symmetric_encryption/railties/symmetric_encryption_validator'
|
17
|
-
end
|
18
17
|
|
19
|
-
|
20
|
-
if defined?(
|
21
|
-
|
22
|
-
end
|
18
|
+
require 'symmetric_encryption/extensions/active_record/base' if defined?(ActiveRecord::Base)
|
19
|
+
require 'symmetric_encryption/railties/symmetric_encryption_validator' if defined?(ActiveModel)
|
20
|
+
require 'symmetric_encryption/mongoid' if defined?(Mongoid)
|
@@ -9,10 +9,6 @@ module ActiveRecord #:nodoc:
|
|
9
9
|
# * Symbolic names of each method to create which has a corresponding
|
10
10
|
# method already defined in rails starting with: encrypted_
|
11
11
|
# * Followed by an optional hash:
|
12
|
-
# :marshal [true|false]
|
13
|
-
# Whether this element should be converted to YAML before encryption
|
14
|
-
# Default: false
|
15
|
-
#
|
16
12
|
# :random_iv [true|false]
|
17
13
|
# Whether the encrypted value should use a random IV every time the
|
18
14
|
# field is encrypted.
|
@@ -29,6 +25,10 @@ module ActiveRecord #:nodoc:
|
|
29
25
|
# Default: false
|
30
26
|
# Highly Recommended where feasible: true
|
31
27
|
#
|
28
|
+
# :type [Symbol]
|
29
|
+
# The type for this field, #see SymmetricEncryption::COERCION_TYPES
|
30
|
+
# Default: :string
|
31
|
+
#
|
32
32
|
# :compress [true|false]
|
33
33
|
# Whether to compress str before encryption
|
34
34
|
# Should only be used for large strings since compression overhead and
|
@@ -41,20 +41,38 @@ module ActiveRecord #:nodoc:
|
|
41
41
|
# Ignore failures since the table may not yet actually exist
|
42
42
|
define_attribute_methods rescue nil
|
43
43
|
|
44
|
-
options
|
45
|
-
random_iv = options.
|
46
|
-
compress = options.
|
47
|
-
|
44
|
+
options = params.last.is_a?(Hash) ? params.pop.dup : {}
|
45
|
+
random_iv = options.delete(:random_iv) || false
|
46
|
+
compress = options.delete(:compress) || false
|
47
|
+
type = options.delete(:type) || :string
|
48
|
+
|
49
|
+
raise "Invalid type: #{type.inspect}. Valid types: #{SymmetricEncryption::COERCION_TYPES.inspect}" unless SymmetricEncryption::COERCION_TYPES.include?(type)
|
50
|
+
|
51
|
+
# For backward compatibility
|
52
|
+
if options.delete(:marshal) == true
|
53
|
+
warn("The :marshal option has been deprecated in favor of :type. For example: attr_encrypted name, :type => :yaml")
|
54
|
+
raise "Marshal is depreacted and cannot be used in conjunction with :type, just use :type. For #{params.inspect}" if type != :string
|
55
|
+
type = :yaml
|
56
|
+
end
|
57
|
+
|
58
|
+
options.each {|option| warn "Ignoring unknown option #{option.inspect} supplied to attr_encrypted with #{params.inspect}"}
|
59
|
+
|
60
|
+
if const_defined?(:EncryptedAttributes, _search_ancestors = false)
|
61
|
+
mod = const_get(:EncryptedAttributes)
|
62
|
+
else
|
63
|
+
mod = const_set(:EncryptedAttributes, Module.new)
|
64
|
+
include mod
|
65
|
+
end
|
48
66
|
|
49
67
|
params.each do |attribute|
|
50
68
|
# Generate unencrypted attribute with getter and setter
|
51
|
-
|
69
|
+
mod.module_eval(<<-UNENCRYPTED, __FILE__, __LINE__ + 1)
|
52
70
|
# Returns the decrypted value for the encrypted attribute
|
53
71
|
# The decrypted value is cached and is only decrypted if the encrypted value has changed
|
54
72
|
# If this method is not called, then the encrypted value is never decrypted
|
55
73
|
def #{attribute}
|
56
74
|
if @stored_encrypted_#{attribute} != self.encrypted_#{attribute}
|
57
|
-
@#{attribute} = ::SymmetricEncryption.decrypt(self.encrypted_#{attribute}).freeze
|
75
|
+
@#{attribute} = ::SymmetricEncryption.decrypt(self.encrypted_#{attribute},version=nil,:#{type}).freeze
|
58
76
|
@stored_encrypted_#{attribute} = self.encrypted_#{attribute}
|
59
77
|
end
|
60
78
|
@#{attribute}
|
@@ -63,7 +81,7 @@ module ActiveRecord #:nodoc:
|
|
63
81
|
# Set the un-encrypted attribute
|
64
82
|
# Also updates the encrypted field with the encrypted value
|
65
83
|
def #{attribute}=(value)
|
66
|
-
self.encrypted_#{attribute} = @stored_encrypted_#{attribute} = ::SymmetricEncryption.encrypt(value
|
84
|
+
self.encrypted_#{attribute} = @stored_encrypted_#{attribute} = ::SymmetricEncryption.encrypt(value,#{random_iv},#{compress},:#{type})
|
67
85
|
@#{attribute} = value.freeze
|
68
86
|
end
|
69
87
|
UNENCRYPTED
|
@@ -17,8 +17,15 @@
|
|
17
17
|
#
|
18
18
|
# field :name, :type => String
|
19
19
|
# field :encrypted_social_security_number, :type => String, :encrypted => true
|
20
|
-
# field :
|
20
|
+
# field :date_of_birth, :type => Date
|
21
21
|
# field :encrypted_life_history, :type => String, :encrypted => {:compress => true, :random_iv => true}
|
22
|
+
#
|
23
|
+
# # Encrypted fields are _always_ stored in Mongo as a String
|
24
|
+
# # To get the result back as an Integer, Symmetric Encryption can do the
|
25
|
+
# # necessary conversions by specifying the internal type as an option
|
26
|
+
# # to :encrypted
|
27
|
+
# # #see SymmetricEncryption::COERCION_TYPES for full list of types
|
28
|
+
# field :encrypted_age, :type => String, :encrypted => {:type => :integer, :random_iv => true}
|
22
29
|
# end
|
23
30
|
#
|
24
31
|
# The above document results in the following document in the Mongo collection 'persons':
|
@@ -67,9 +74,13 @@
|
|
67
74
|
# @param [ Hash ] options The options to pass to the field.
|
68
75
|
#
|
69
76
|
# @option options [ Boolean | Hash ] :encrypted If the field contains encrypted data.
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
77
|
+
# When :encrypted is a Hash it consists of:
|
78
|
+
# @option options [ Symbol ] :type The type for this field, #see SymmetricEncryption::COERCION_TYPES
|
79
|
+
# @option options [ Boolean ] :random_iv Whether the encrypted value should use a random IV every time the field is encrypted.
|
80
|
+
# @option options [ Boolean ] :compress Whether to compress this encrypted field
|
81
|
+
# @option options [ Symbol ] :decrypt_as Name of the getters and setters to generate to access the decrypted value of this field.
|
82
|
+
#
|
83
|
+
# Some of the other regular Mongoid options:
|
73
84
|
#
|
74
85
|
# @option options [ Class ] :type The type of the field.
|
75
86
|
# @option options [ String ] :label The label for the field.
|
@@ -90,14 +101,25 @@ Mongoid::Fields.option :encrypted do |model, field, options|
|
|
90
101
|
|
91
102
|
random_iv = options.delete(:random_iv) || false
|
92
103
|
compress = options.delete(:compress) || false
|
104
|
+
type = options.delete(:type) || :string
|
105
|
+
raise "Invalid type: #{type.inspect}. Valid types: #{SymmetricEncryption::COERCION_TYPES.inspect}" unless SymmetricEncryption::COERCION_TYPES.include?(type)
|
106
|
+
|
107
|
+
options.each {|option| warn "Ignoring unknown option #{option.inspect} supplied to Mongoid :encrypted for #{model}##{field}"}
|
108
|
+
|
109
|
+
if model.const_defined?(:EncryptedAttributes, _search_ancestors = false)
|
110
|
+
mod = model.const_get(:EncryptedAttributes)
|
111
|
+
else
|
112
|
+
mod = model.const_set(:EncryptedAttributes, Module.new)
|
113
|
+
model.send(:include, mod)
|
114
|
+
end
|
93
115
|
|
94
116
|
# Generate getter and setter methods
|
95
|
-
|
117
|
+
mod.module_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
96
118
|
# Set the un-encrypted field
|
97
119
|
# Also updates the encrypted field with the encrypted value
|
98
120
|
# Freeze the decrypted field value so that it is not modified directly
|
99
121
|
def #{decrypted_field_name}=(value)
|
100
|
-
self.#{encrypted_field_name} = @stored_#{encrypted_field_name} = ::SymmetricEncryption.encrypt(value,#{random_iv},#{compress})
|
122
|
+
self.#{encrypted_field_name} = @stored_#{encrypted_field_name} = ::SymmetricEncryption.encrypt(value,#{random_iv},#{compress},:#{type})
|
101
123
|
@#{decrypted_field_name} = value.freeze
|
102
124
|
end
|
103
125
|
|
@@ -106,7 +128,7 @@ Mongoid::Fields.option :encrypted do |model, field, options|
|
|
106
128
|
# If this method is not called, then the encrypted value is never decrypted
|
107
129
|
def #{decrypted_field_name}
|
108
130
|
if @stored_#{encrypted_field_name} != self.#{encrypted_field_name}
|
109
|
-
@#{decrypted_field_name} = ::SymmetricEncryption.decrypt(self.#{encrypted_field_name}).freeze
|
131
|
+
@#{decrypted_field_name} = ::SymmetricEncryption.decrypt(self.#{encrypted_field_name},version=nil,:#{type}).freeze
|
110
132
|
@stored_#{encrypted_field_name} = self.#{encrypted_field_name}
|
111
133
|
end
|
112
134
|
@#{decrypted_field_name}
|
@@ -8,7 +8,11 @@ namespace :symmetric_encryption do
|
|
8
8
|
|
9
9
|
desc 'Encrypt a value, such as a password. Example: rake symmetric_encryption:encrypt'
|
10
10
|
task :encrypt => :environment do
|
11
|
-
|
11
|
+
begin
|
12
|
+
require 'highline'
|
13
|
+
rescue LoadError
|
14
|
+
raise "Please install gem highline before using the command line task to encrypt an entered string.\n gem install \"highline\""
|
15
|
+
end
|
12
16
|
password1 = nil
|
13
17
|
password2 = 0
|
14
18
|
|
@@ -14,6 +14,20 @@ module SymmetricEncryption
|
|
14
14
|
@@secondary_ciphers = []
|
15
15
|
@@select_cipher = nil
|
16
16
|
|
17
|
+
# List of types supported when encrypting or decrypting data
|
18
|
+
#
|
19
|
+
# Each type maps to the built-in Ruby types as follows:
|
20
|
+
# :string => String
|
21
|
+
# :integer => Integer
|
22
|
+
# :float => Float
|
23
|
+
# :decimal => BigDecimal
|
24
|
+
# :datetime => DateTime
|
25
|
+
# :time => Time
|
26
|
+
# :date => Date
|
27
|
+
# :json => Uses JSON serialization, useful for hashes and arrays
|
28
|
+
# :yaml => Uses YAML serialization, useful for hashes and arrays
|
29
|
+
COERCION_TYPES = [:string, :integer, :float, :decimal, :datetime, :time, :date, :boolean, :json, :yaml]
|
30
|
+
|
17
31
|
# Set the Primary Symmetric Cipher to be used
|
18
32
|
#
|
19
33
|
# Example: For testing purposes the following test cipher can be used:
|
@@ -58,8 +72,8 @@ module SymmetricEncryption
|
|
58
72
|
end
|
59
73
|
|
60
74
|
# AES Symmetric Decryption of supplied string
|
61
|
-
# Returns decrypted
|
62
|
-
# Returns nil if the supplied
|
75
|
+
# Returns decrypted value
|
76
|
+
# Returns nil if the supplied value is nil
|
63
77
|
# Returns "" if it is a string and it is empty
|
64
78
|
#
|
65
79
|
# Parameters
|
@@ -68,6 +82,13 @@ module SymmetricEncryption
|
|
68
82
|
# version
|
69
83
|
# Specify which cipher version to use if no header is present on the
|
70
84
|
# encrypted string
|
85
|
+
# type [:string|:integer|:float|:decimal|:datetime|:time|:date|:boolean]
|
86
|
+
# If value is set to something other than :string, then the coercible gem
|
87
|
+
# will be use to coerce the unencrypted string value into the specified
|
88
|
+
# type. This assumes that the value was stored using the same type.
|
89
|
+
# Note: If type is set to something other than :string, it's expected
|
90
|
+
# that the coercible gem is available in the path.
|
91
|
+
# Default: :string
|
71
92
|
#
|
72
93
|
# If the supplied string has an encryption header then the cipher matching
|
73
94
|
# the version number in the header will be used to decrypt the string
|
@@ -84,7 +105,7 @@ module SymmetricEncryption
|
|
84
105
|
# yet significant number of cases it is possible to decrypt data using
|
85
106
|
# the incorrect key. Clearly the data returned is garbage, but it still
|
86
107
|
# successfully returns a string of data
|
87
|
-
def self.decrypt(encrypted_and_encoded_string, version=nil)
|
108
|
+
def self.decrypt(encrypted_and_encoded_string, version=nil, type=:string)
|
88
109
|
raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher
|
89
110
|
return encrypted_and_encoded_string if encrypted_and_encoded_string.nil? || (encrypted_and_encoded_string == '')
|
90
111
|
|
@@ -109,7 +130,7 @@ module SymmetricEncryption
|
|
109
130
|
decrypted.force_encoding(SymmetricEncryption::BINARY_ENCODING)
|
110
131
|
end
|
111
132
|
end
|
112
|
-
decrypted
|
133
|
+
coerce_from_string(decrypted, type)
|
113
134
|
end
|
114
135
|
|
115
136
|
# AES Symmetric Encryption of supplied string
|
@@ -118,7 +139,7 @@ module SymmetricEncryption
|
|
118
139
|
# Returns "" if it is a string and it is empty
|
119
140
|
#
|
120
141
|
# Parameters
|
121
|
-
#
|
142
|
+
# value [Object]
|
122
143
|
# String to be encrypted. If str is not a string, #to_s will be called on it
|
123
144
|
# to convert it to a string
|
124
145
|
#
|
@@ -145,11 +166,20 @@ module SymmetricEncryption
|
|
145
166
|
# compression
|
146
167
|
# Note: Adds a 6 byte header prior to encoding, only if :random_iv is false
|
147
168
|
# Default: false
|
148
|
-
|
169
|
+
#
|
170
|
+
# type [:string|:integer|:float|:decimal|:datetime|:time|:date|:boolean]
|
171
|
+
# Expected data type of the value to encrypt
|
172
|
+
# Uses the coercible gem to coerce non-string values into string values.
|
173
|
+
# When type is set to :string (the default), uses #to_s to convert
|
174
|
+
# non-string values to string values.
|
175
|
+
# Note: If type is set to something other than :string, it's expected that
|
176
|
+
# the coercible gem is available in the path.
|
177
|
+
# Default: :string
|
178
|
+
def self.encrypt(str, random_iv=false, compress=false, type=:string)
|
149
179
|
raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher
|
150
180
|
|
151
181
|
# Encrypt and then encode the supplied string
|
152
|
-
@@cipher.encrypt(str, random_iv, compress)
|
182
|
+
@@cipher.encrypt(coerce_to_string(str, type), random_iv, compress)
|
153
183
|
end
|
154
184
|
|
155
185
|
# Invokes decrypt
|
@@ -425,6 +455,63 @@ module SymmetricEncryption
|
|
425
455
|
Cipher.new(config)
|
426
456
|
end
|
427
457
|
|
458
|
+
# Uses coercible gem to coerce values from strings into the target type
|
459
|
+
# Note: if the type is :string, then the value is returned as is, and the
|
460
|
+
# coercible gem is not used at all.
|
461
|
+
def self.coerce_from_string(value, type)
|
462
|
+
return if value.nil?
|
463
|
+
case type
|
464
|
+
when :string
|
465
|
+
value
|
466
|
+
when :json
|
467
|
+
JSON.load(value)
|
468
|
+
when :yaml
|
469
|
+
YAML.load(value)
|
470
|
+
else
|
471
|
+
coercer = Coercible::Coercer.new
|
472
|
+
coercion_method = "to_#{type}".to_sym
|
473
|
+
coercer[String].send(coercion_method, value)
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
# Uses coercible gem to coerce values to strings from the specified type
|
478
|
+
# Note: if the type is :string, and value is not nil, then #to_s is called
|
479
|
+
# on the value and the coercible gem is not used at all.
|
480
|
+
def self.coerce_to_string(value, type)
|
481
|
+
return if value.nil?
|
482
|
+
|
483
|
+
case type
|
484
|
+
when :string
|
485
|
+
value.to_s
|
486
|
+
when :json
|
487
|
+
value.to_json
|
488
|
+
when :yaml
|
489
|
+
value.to_yaml
|
490
|
+
else
|
491
|
+
coercer = Coercible::Coercer.new
|
492
|
+
coercer[coercion_type(type, value)].to_string(value)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
# Returns the correct coercion type to use for the specified symbol and value
|
497
|
+
def self.coercion_type(symbol, value)
|
498
|
+
if symbol == :boolean
|
499
|
+
value.class
|
500
|
+
else
|
501
|
+
COERCION_TYPE_MAP[symbol]
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
COERCION_TYPE_MAP = {
|
506
|
+
:string => String,
|
507
|
+
:integer => Integer,
|
508
|
+
:float => Float,
|
509
|
+
:decimal => BigDecimal,
|
510
|
+
:datetime => DateTime,
|
511
|
+
:time => Time,
|
512
|
+
:date => Date
|
513
|
+
}
|
514
|
+
|
428
515
|
# With Ruby 1.9 strings have encodings
|
429
516
|
if defined?(Encoding)
|
430
517
|
BINARY_ENCODING = Encoding.find("binary")
|