symmetric-encryption 3.2 → 3.3
Sign up to get free protection for your applications and to get access to all the features.
- 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")
|