yattr_encrypted 0.1.1

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.
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rake'
4
+ gem 'pry'
5
+ gem 'minitest'
6
+ gem 'activerecord'
7
+ gem 'mocha'
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Sean Huber - shuber@huberry.com
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.mdown ADDED
@@ -0,0 +1,149 @@
1
+ # YattrEncrypted #
2
+
3
+ Version: 0.1.0 (but you should check lib/yattr_encrypted/version.rb to be sure)
4
+
5
+ ## Applicability ##
6
+
7
+ This code has been tested
8
+
9
+ * using ruby 1.9.2-p290
10
+ * stand alone
11
+ * Rails 3.1.4
12
+
13
+ ## Description ##
14
+
15
+ This is based on code stolen from *yattr_encrypted* and *encryptor* - both by
16
+ github.com/shuber - and both very fine gems.
17
+
18
+ So why another dumb attribute encryptor gem?
19
+
20
+ shuber's gems don't do what I want and they appear to be dormant.
21
+
22
+ The primary difference between **shuber**'s gems and this one is a matter of
23
+ flexibility and simplicity. **yattr_encrypted** is simple, easy to use and
24
+ (should be) pretty secure. This comes at the expense of *flexibility*.
25
+
26
+ In **yattr_encrypted** you do not have a choice of algorithm, encoding, additional
27
+ encrypt/decrypt methods, conditional encryption, or underlying data mapper.
28
+ You also have to use Rails 3.1+.
29
+
30
+ In more detail, here is where they differ:
31
+
32
+ **yattr_encrypted** does not support:
33
+
34
+ * DataMapper
35
+ * Sequel
36
+ * ActiveRecord find_by*** methods for encrypted fields
37
+ * alternate encryption methods
38
+ * alternate encryption algorithms
39
+ * String#encrypt, #encrypt!, #decrypt, and #decrypt!
40
+ * most of the options **yattr_encrypted** supports
41
+ * conditional encrypting - **yattr_encrypted** supports conditionally encrypting
42
+ fields based on some logic. I don't have a use case for this, so **yattr_encrypted**
43
+ does not support it.
44
+
45
+ **yattr_encrypted** is also self contained - only relies on the *openssl* (part
46
+ of the Ruby Standard Library), whereas the *shuber* gem depends on *encryptor*.
47
+
48
+ What **yattr_encrypted** *does* support:
49
+
50
+ * **yattr_encrypted** ONLY works with ActiveRecord
51
+ * random initial values for each encrypted attribute. This is done by creating a
52
+ random *iv* and including it in the encrypted data. See *openssl* documentation
53
+ for details [OpenSSL::Cipher]
54
+ * detects when fields are modified by actions other than assignment. This supports
55
+ encrypting complex types - such as hashes and arrays. This is implemented by adding
56
+ *save* and *save!* methods to models
57
+ * Rails 3.1 & Rails 3.2 - doesn't pretend to support anything lower (but it might work)
58
+
59
+
60
+ ## Installation ##
61
+
62
+ Either
63
+
64
+ gem install yattr_encrypted
65
+
66
+ Or add to your Gemfile
67
+
68
+ gem yattr_encrypted
69
+
70
+ ## Usage ##
71
+
72
+ Name each field you want to encrypt in the `yattr_encrypted` macro.
73
+
74
+ For example, assume that you want to encrypt a field named `foo`. Then create
75
+ a field in your *migration* named `foo_encrypted`.
76
+
77
+ class Foo < ActiveRecord::Base
78
+ yattr_encrypted :foo
79
+ end
80
+
81
+ This will add accessor methods `foo` and `foo=` to your model. You do not use the
82
+ actual `foo_encrypted` field directly.
83
+
84
+ NOTE: `foo` is not useable for search. If you want to search on encrypted fields,
85
+ use **attr\_encrypted**. **yattr\_encrypted** does not create searchable fields because
86
+ it automatically generates random initial values so it is not possible to generate
87
+ matching encrypted values without retrieving the encrypted data.
88
+
89
+ ### Options ###
90
+
91
+ The encrypted field name defaults to `<field>_encrypted`. You can change this on
92
+ a field by field basis using the `:prefix` and `:suffix` - which define strings
93
+ which are prefixed and suffixed to the field name to create the encrypted field name.
94
+ They default to:
95
+
96
+ :prefix = ''
97
+ :suffix = '_encrypted'
98
+
99
+ Notice that the underscore ('_') must be included in `:prefix` and `:suffix`, if you
100
+ want them.
101
+
102
+ ### Encryption keys
103
+
104
+ The default encryption key is the value of `<application>::Application.config.secret_token`
105
+ which is in `config/initializers/secret_token.rb`.
106
+
107
+ If you want to use some other key - on a field by field basis,
108
+ you can specify the key on a field by field basis using the `:key` option.
109
+
110
+ NOTE: all encryption uses `ase-256-cbc` with random initial values. For some reason this
111
+ triggers a key length check in **openssl** which raises an exception if your key is
112
+ too short. I don't know what the required key length is, but `secrete_token` is long enough
113
+ and 'this is a very long secret key' is not.
114
+
115
+ If you supply your own key, it can be a String or a Proc which returns a String.
116
+
117
+ ### Initial Values ###
118
+
119
+ As stated everywhere - random initial values are automatically generated for all fields.
120
+ They are prepended to the actual encrypted data and stripped during decryption. You can't
121
+ override this, nor can you provide your own initial values.
122
+
123
+ ### Encryption Method ###
124
+
125
+ **yattr\_encrypted** only uses `aes-256-cbc`. If you want variety, use **attr\_encrypted**, which
126
+ supports the entire gamete supplied by **openssl**
127
+
128
+ ### Encoding of Encrypted Data and Database Compatibility ###
129
+
130
+ All data saved in the database is `base64` encode. All fields are further
131
+ serialized as JSON objects. This is to avoid dealing with any database idiodicy
132
+ and to transparently handle complex data types being used in database fields [such as
133
+ Hashes and Arrays].
134
+
135
+ The encoding algorithm for a field is:
136
+
137
+ field_jsonified = ActiveSupprt::JSON.encode field
138
+ iv = lambda { (0..16).map { |x| rand(256).chr }.join() }.call
139
+ encrypted_data = YattrEncrypted.encrypt field_jsonified, iv
140
+ field_encrypted = ActiveSupport::Base64.encode64 ("%04d" % iv.length) + iv + encrypted_data
141
+
142
+ The decoding algorithm is:
143
+
144
+ field_decoded = ActiveSupport::Base64::decode64 field_encrypted
145
+ len = field_decoded[0..3].to_i + 1
146
+ iv = field_decoded[4..(len)]
147
+ encrypted_data = field_decoded[(len+1)..-1]
148
+ field_jsonified = YattrEncrypted.decrypt encrypted_data, iv
149
+ field = ActiveSupprt::JSON.decode field_jsonified
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ # require 'rake/testtask'
3
+ # require 'rdoc/task'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the yattr_encrypted gem.'
9
+ task :test do
10
+ if ENV['TEST']
11
+ system "ruby #{ENV['TEST']}"
12
+ else
13
+ system "ruby test/*_test.rb"
14
+ end
15
+ end
16
+ # Rake::TestTask.new(:test) do |t|
17
+ # t.libs << 'lib'
18
+ # # t.pattern = 'test/**/*_test.rb'
19
+ # t.pattern = ENV['TEST'] ? ENV['TEST'] : 'test/**/*_test.rb'
20
+ # t.verbose = true
21
+ # end
22
+
23
+ desc 'Build Gem'
24
+ task 'gem' do
25
+ system 'gem build yattr_encrypted.gemspec'
26
+ end
27
+
28
+ desc 'Generate documentation for the yattr_encrypted gem.'
29
+ task 'rdoc' do
30
+ system 'rdoc README.rdoc lib/'
31
+ end
32
+ # RDoc::Task.new do |rdoc|
33
+ # rdoc.rdoc_dir = 'rdoc'
34
+ # rdoc.title = 'yattr_encrypted'
35
+ # rdoc.options << '--line-numbers' << '--inline-source'
36
+ # rdoc.rdoc_files.include('README*')
37
+ # rdoc.rdoc_files.include('lib/**/*.rb')
38
+ # end
@@ -0,0 +1,11 @@
1
+ module YattrEncrypted
2
+ class Railtie < Rails::Railtie
3
+ initializer "active_record.initialize_yattr_encrypted" do
4
+ ActiveSupport.on_load(:active_record) do
5
+ class ActiveRecord::Base
6
+ include YattrEncrypted
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module YattrEncrypted
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,261 @@
1
+ require 'openssl'
2
+ require 'active_support'
3
+ require 'base64'
4
+ require 'yattr_encrypted/railtie' if defined?(Rails)
5
+ require 'yattr_encrypted/version'
6
+
7
+ # how to use
8
+ #
9
+ # class Foo < ActiveRecord::Base
10
+ # yattr_encrypted :foo, :bar
11
+ # end
12
+
13
+ # Adds attr_accessors that encrypt and decrypt an object's attributes
14
+ module YattrEncrypted
15
+
16
+ ALGORITHM = 'aes-256-cbc'
17
+
18
+ # Generates attr_accessors that encrypt and decrypt attributes transparently
19
+ #
20
+ # Options (any other options you specify are passed to the
21
+ # encryptor's encrypt and decrypt methods)
22
+ #
23
+ # :prefix A prefix used to generate the name of the referenced
24
+ # encrypted attributes. For example <tt>attr_accessor
25
+ # :email, :password, :prefix => 'crypted_'</tt> would
26
+ # generate attributes named 'crypted_email' and
27
+ # 'crypted_password' to store the encrypted email and
28
+ # password. Defaults to ''.
29
+ #
30
+ # :suffix A suffix used to generate the name of the referenced
31
+ # encrypted attributes. For example <tt>attr_accessor
32
+ # :email, :password, :prefix => '', :suffix =>
33
+ # '_encrypted'</tt> would generate attributes named
34
+ # 'email_encrypted' and 'password_encrypted' to store the
35
+ # encrypted email. Defaults to '_encrypted'.
36
+ #
37
+ # :key The encryption key. Not generally required.
38
+ # Defaults to Rails.application.config.secret_token
39
+ #
40
+ # Example
41
+ #
42
+ # class User
43
+ # yattr_encrypted :email
44
+ # end
45
+ #
46
+ # @user = User.new
47
+ # @user.encrypted_email # nil? is true
48
+ # @user.email? # false
49
+ # @user.email = 'test@example.com'
50
+ # @user.email? # true
51
+ # @user.encrypted_email # returns the encrypted version of 'test@example.com'
52
+ #
53
+
54
+ def self.included(base)
55
+ class << base
56
+ attr_accessor :yate_encrypted_attributes
57
+
58
+ def yattr_encrypted(*attributes)
59
+ # construct options
60
+ options = {
61
+ :prefix => '',
62
+ :suffix => '_encrypted',
63
+ :key => defined?(::Rails) ? ::Rails.application.config.secret_token : nil,
64
+ }
65
+ # merge specific options
66
+ options.merge!(attributes.pop) if Hash === attributes.last
67
+
68
+ # tell self to define instance methods from the database if they have not already been generated
69
+ define_attribute_methods unless attribute_methods_generated?
70
+
71
+ # collect existing instance methods
72
+ instance_methods_as_symbols = instance_methods.map { |method| method.to_sym }
73
+
74
+ # iterate through attributes
75
+ attributes.map { |x| x.to_sym }.each do |attribute|
76
+ encrypted_attribute_name = [options[:prefix], attribute, options[:suffix]].join.to_sym
77
+
78
+ # barf if reader and write doesn't exist for encrypted attribute
79
+ raise ArgumentError.new("No Reader method for encrypted version of #{attribute}: #{encrypted_attribute_name}") \
80
+ unless instance_methods_as_symbols.include?(encrypted_attribute_name)
81
+ raise ArgumentError.new("No Write method for encrypted version of #{attribute}: #{encrypted_attribute_name}") \
82
+ unless instance_methods_as_symbols.include?(:"#{encrypted_attribute_name}=")
83
+
84
+ tmp =<<-XXX
85
+ puts "defining #{attribute}"
86
+ def #{attribute}
87
+ unless @#{attribute} && !@#{attribute}.empty?
88
+ options = yate_encrypted_attributes[:#{attribute}]
89
+ @#{attribute} = #{encrypted_attribute_name} ? \
90
+ yate_decrypt(#{encrypted_attribute_name}, options[:key]) : \
91
+ ''
92
+ self.yate_checksums[:#{attribute}] = yate_field_hash_value(:#{attribute})
93
+ self.yate_dirty[:#{attribute}] = true
94
+ end
95
+ @#{attribute}
96
+ end
97
+ XXX
98
+ class_eval(tmp)
99
+
100
+ tmp =<<-XXX
101
+ puts "self: #{self}"
102
+ def #{attribute}= value
103
+ @#{attribute} = value
104
+ options = yate_encrypted_attributes[:#{attribute}]
105
+ self.#{encrypted_attribute_name} = yate_encrypt(value, options[:key])
106
+ self.yate_checksums[:#{attribute}] = yate_field_hash_value(:#{attribute})
107
+ self.yate_dirty[:#{attribute}] = true
108
+ end
109
+ XXX
110
+ class_eval(tmp)
111
+
112
+ define_method("#{attribute}?") do
113
+ value = send(attribute)
114
+ value.respond_to?(:empty?) ? !value.empty? : !!value
115
+ end
116
+
117
+ self.yate_encrypted_attributes ||= {}
118
+
119
+ self.yate_encrypted_attributes[attribute.to_sym] = \
120
+ options.merge(:attribute => encrypted_attribute_name)
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ # Checks if an attribute is configured with <tt>yattr_encrypted</tt>
127
+ def yattr_encrypted?(attribute)
128
+ self.class.yate_encrypted_attributes.has_key?(attribute.to_sym)
129
+ end
130
+
131
+ def save *args
132
+ yate_update_encrypted_values
133
+ super
134
+ end
135
+
136
+ def save! *args
137
+ yate_update_encrypted_values
138
+ super
139
+ end
140
+
141
+ def update_attribute attribute, value
142
+ if (options = yate_encrypted_attributes[attribute])
143
+ self.send "#{attribute}=".to_sym, value
144
+ update_attribute options[:attribute], self.send(options[:attribute]) if yate_field_changed? attribute
145
+ else
146
+ super
147
+ end
148
+ end
149
+
150
+ def update_attributes params, options = {}
151
+ tmp = {}
152
+ params.keys.each do |attribute|
153
+ if (options = yate_encrypted_attributes[attribute])
154
+ self.send "#{attribute}=", params[attribute]
155
+ tmp[options[:attribute]] = self.send options[:attribute]
156
+ else
157
+ tmp[attribute] = params[attribute]
158
+ end
159
+ end
160
+ params = tmp
161
+ super
162
+ end
163
+
164
+ # protected methods - nobody needs to use these outside of the model
165
+ protected
166
+
167
+ def yate_checksums
168
+ @yate_checksums ||= {}
169
+ end
170
+
171
+ def yate_dirty
172
+ @yate_dirty ||= {}
173
+ end
174
+
175
+ def yate_encrypted_attributes
176
+ self.class.yate_encrypted_attributes
177
+ end
178
+
179
+ private
180
+
181
+ # yate_encrypt(value, key)
182
+ #
183
+ def yate_encrypt(value, key)
184
+ cipher = OpenSSL::Cipher.new(ALGORITHM)
185
+ cipher.encrypt
186
+ cipher.key = key
187
+ iv = cipher.random_iv
188
+
189
+ # jsonify data
190
+ value_marshalled = Marshal.dump value
191
+
192
+ # encrypt data
193
+ result = cipher.update value_marshalled
194
+ result << cipher.final
195
+
196
+ # return encrypted data and iv
197
+ Base64.encode64(("%04d" % iv.length) + iv + result)
198
+ end
199
+
200
+ # yate_decrypt(encrypted_value, key)
201
+ def yate_decrypt(marshalled_value, key)
202
+ # initialize decryptor
203
+ cipher = OpenSSL::Cipher.new(ALGORITHM)
204
+ cipher.decrypt
205
+ cipher.key = key
206
+
207
+ # extract encrypted_value
208
+ encrypted_value = Base64.decode64 marshalled_value
209
+
210
+ # extract and set iv
211
+ iv_end = encrypted_value[0..3].to_i + 3
212
+ cipher.iv = encrypted_value[4..(iv_end)]
213
+ encrypted_value = encrypted_value[(iv_end+1)..-1]
214
+
215
+ # derypte and return
216
+ result = cipher.update(encrypted_value)
217
+ result << cipher.final
218
+
219
+ Marshal.load result
220
+ end
221
+
222
+ # support for fields which are not atomic values
223
+ def yate_field_hash_value(attribute)
224
+ attribute = attribute.to_s if Symbol === attribute
225
+ OpenSSL::HMAC.digest('md5', 'ersatz key', Marshal.dump(self.instance_variable_get("@#{attribute}")))
226
+ end
227
+
228
+ def yate_field_changed?(attribute)
229
+ attribute = attribute.to_sym unless Symbol === attribute
230
+ yate_field_hash_value(attribute) != self.yate_checksums[attribute] || self.yate_dirty[attribute]
231
+ end
232
+
233
+ def yate_update_encrypted_values
234
+ yate_encrypted_attributes.each do |attribute, options|
235
+ if yate_field_changed?(attribute)
236
+ self.send "#{options[:attribute]}=".to_sym, yate_encrypt(self.send(attribute), options[:key])
237
+ yate_dirty.delete(attribute)
238
+ end
239
+ end
240
+ end
241
+
242
+ # protected
243
+ #
244
+ # # Returns yattr_encrypted options evaluated in the current object's scope for the attribute specified
245
+ # def yate_evaluated_options_for(attribute)
246
+ # self.class.encrypted_attributes[attribute.to_sym].inject({}) do |hash, (option, value)|
247
+ # hash.merge!(option => yate_evaluate_option(value))
248
+ # end
249
+ # end
250
+ #
251
+ # # Evaluates symbol (method reference) or proc (responds to call) options
252
+ # #
253
+ # # If the option is not a proc then the original option is returned
254
+ # def yate_evaluate_option(option)
255
+ # if option.respond_to?(:call)
256
+ # option.call(self)
257
+ # else
258
+ # option
259
+ # end
260
+ # end
261
+ end
@@ -0,0 +1,8 @@
1
+ require 'pry'
2
+ require 'minitest/autorun'
3
+ require 'digest/sha2'
4
+ require 'rubygems'
5
+
6
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ $:.unshift(File.dirname(__FILE__))
8
+ require 'yattr_encrypted'
@@ -0,0 +1,66 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ # require 'active_record'
4
+ require 'yattr_encrypted'
5
+
6
+ module ActiveRecord
7
+ class Base
8
+ include YattrEncrypted
9
+
10
+ def self.attribute_methods_generated?
11
+ true
12
+ end
13
+
14
+ def save
15
+ true
16
+ end
17
+
18
+ def save!
19
+ true
20
+ end
21
+
22
+ def update_attribute attribute, value
23
+ true
24
+ end
25
+
26
+ def update_attributes attribute_hash, options
27
+ true
28
+ end
29
+ end
30
+ end
31
+
32
+ class SomeClass < ActiveRecord::Base
33
+ attr_accessor :field_encrypted
34
+ yattr_encrypted :field, :key => 'a honkin big key: honk honk honk honk honk'
35
+ end
36
+
37
+ class TestYattrEncrypted < MiniTest::Unit::TestCase
38
+ def setup
39
+ @sc = SomeClass.new
40
+ end
41
+
42
+ def test_yattr_encrypted_should_create_accessors
43
+ assert @sc.respond_to?(:field), "a SomeClass instance should respond to :field"
44
+ assert @sc.respond_to?(:field=), "a SomeClass instance should respond to :field="
45
+ assert @sc.respond_to?(:field?), "a SomeClass instance should respond to :field?"
46
+ assert @sc.respond_to?(:yate_encrypted_attributes), "a SomeClass instance should respond to :yate_encrypted_attributes"
47
+ end
48
+
49
+ def test_assigning_field_should_assign_field_encrypted
50
+ assert_nil @sc.field_encrypted, "field_encrypted should be nil prior to assignment to field"
51
+ @sc.field = 'a field value'
52
+ refute_nil @sc.field_encrypted, "field_encrypted should not be nil"
53
+ assert_equal 'a field value', @sc.field, "@sc.field should match input"
54
+ options = @sc.send(:yate_encrypted_attributes)[:field]
55
+ assert_equal 'a field value', @sc.send(:yate_decrypt, @sc.field_encrypted, options[:key]), \
56
+ "decrypting @sc.field_encrypted should match input"
57
+ end
58
+
59
+ def test_save_should_update_encrypted
60
+ @sc.field = { key: 'value' }
61
+ @sc.save
62
+ options = @sc.send(:yate_encrypted_attributes)[:field]
63
+ decrypted = @sc.send(:yate_decrypt, @sc.field_encrypted, options[:key])
64
+ assert_equal( { key: 'value' }, decrypted, "decrypt @sc.field_encrypted should be correct")
65
+ end
66
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yattr_encrypted
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mike Howard
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-18 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: pry
16
+ requirement: &2151816860 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2151816860
25
+ description: Generates yattr_accessors that encrypt and decrypt attributes transparently.
26
+ Based on attr_encrypted by Sean Huber [https://github.com/shuber]
27
+ email: mike@clove.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - lib/yattr_encrypted/railtie.rb
33
+ - lib/yattr_encrypted/version.rb
34
+ - lib/yattr_encrypted.rb
35
+ - MIT-LICENSE
36
+ - Rakefile
37
+ - README.mdown
38
+ - Gemfile
39
+ - test/test_helper.rb
40
+ - test/yattr_encrypted_test.rb
41
+ homepage: http://github.mikhoward/yattr_encrypted
42
+ licenses: []
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --line-numbers
46
+ - --inline-source
47
+ - --main
48
+ - README.mdown
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ segments:
58
+ - 0
59
+ hash: -3522761952983170560
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 1.8.6
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Encrypt and decrypt attributes
72
+ test_files:
73
+ - test/test_helper.rb
74
+ - test/yattr_encrypted_test.rb