yattr_encrypted 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.mdown CHANGED
@@ -1,6 +1,6 @@
1
1
  # YattrEncrypted #
2
2
 
3
- Version: 0.1.2 (but you should check lib/yattr_encrypted/version.rb to be sure)
3
+ Version: 0.1.3 (but you should check lib/yattr_encrypted/version.rb to be sure)
4
4
 
5
5
  ## Applicability ##
6
6
 
@@ -53,7 +53,7 @@ random *iv* and including it in the encrypted data. See *openssl* documentation
53
53
  for details [OpenSSL::Cipher]
54
54
  * detects when fields are modified by actions other than assignment. This supports
55
55
  encrypting complex types - such as hashes and arrays. This is implemented by adding
56
- *save* and *save!* methods to models
56
+ a `before_save` calleback to the private method *yattr_update_encrypted_values*
57
57
  * Rails 3.1 & Rails 3.2 - doesn't pretend to support anything lower (but it might work)
58
58
 
59
59
 
@@ -132,18 +132,40 @@ serialized as JSON objects. This is to avoid dealing with any database idiodicy
132
132
  and to transparently handle complex data types being used in database fields [such as
133
133
  Hashes and Arrays].
134
134
 
135
- The encoding algorithm for a field is:
135
+ The encoding code for a field is:
136
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
137
+ cipher = OpenSSL::Cipher.new(ALGORITHM)
138
+ cipher.encrypt
139
+ cipher.key = key
140
+ iv = cipher.random_iv # ask OpenSSL for a new, random initial value
141
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
142
+ # jsonify data
143
+ value_marshalled = Marshal.dump value
144
+
145
+ # encrypt data
146
+ result = cipher.update value_marshalled
147
+ result << cipher.final
148
+
149
+ # return encrypted data and iv
150
+ Base64.encode64(("%04d" % iv.length) + iv + result)
151
+
152
+ The decoding code is:
153
+
154
+ # initialize decryptor
155
+ cipher = OpenSSL::Cipher.new(ALGORITHM)
156
+ cipher.decrypt
157
+ cipher.key = key
158
+
159
+ # extract encrypted_value
160
+ encrypted_value = Base64.decode64 marshalled_value
161
+
162
+ # extract and set iv
163
+ iv_end = encrypted_value[0..3].to_i + 3
164
+ cipher.iv = encrypted_value[4..(iv_end)]
165
+ encrypted_value = encrypted_value[(iv_end+1)..-1]
166
+
167
+ # derypte and return
168
+ result = cipher.update(encrypted_value)
169
+ result << cipher.final
170
+
171
+ Marshal.load result
@@ -1,3 +1,3 @@
1
1
  module YattrEncrypted
2
- VERSION = '0.1.2'
2
+ VERSION = '0.1.3'
3
3
  end
@@ -67,11 +67,14 @@ module YattrEncrypted
67
67
 
68
68
  # tell self to define instance methods from the database if they have not already been generated
69
69
  define_attribute_methods unless attribute_methods_generated?
70
+
71
+ # schedule yattr_update_encrypted_values before save
72
+ self.send :before_save, :yate_update_encrypted_values unless self.yate_encrypted_attributes
70
73
 
71
74
  # collect existing instance methods
72
75
  instance_methods_as_symbols = instance_methods.map { |method| method.to_sym }
73
76
 
74
- # iterate through attributes
77
+ # iterate through attributes and create accessors, verify encryped accessors exist
75
78
  attributes.map { |x| x.to_sym }.each do |attribute|
76
79
  encrypted_attribute_name = [options[:prefix], attribute, options[:suffix]].join.to_sym
77
80
 
@@ -82,14 +85,13 @@ module YattrEncrypted
82
85
  unless instance_methods_as_symbols.include?(:"#{encrypted_attribute_name}=")
83
86
 
84
87
  tmp =<<-XXX
85
- puts "defining #{attribute}"
86
88
  def #{attribute}
87
89
  unless @#{attribute} && !@#{attribute}.empty?
88
90
  options = yate_encrypted_attributes[:#{attribute}]
89
91
  @#{attribute} = #{encrypted_attribute_name} ? \
90
92
  yate_decrypt(#{encrypted_attribute_name}, options[:key]) : \
91
93
  ''
92
- self.yate_checksums[:#{attribute}] = yate_field_hash_value(:#{attribute})
94
+ self.yate_checksums[:#{attribute}] = yate_attribute_hash_value(:#{attribute})
93
95
  self.yate_dirty[:#{attribute}] = true
94
96
  end
95
97
  @#{attribute}
@@ -98,12 +100,11 @@ module YattrEncrypted
98
100
  class_eval(tmp)
99
101
 
100
102
  tmp =<<-XXX
101
- puts "self: #{self}"
102
103
  def #{attribute}= value
103
104
  @#{attribute} = value
104
105
  options = yate_encrypted_attributes[:#{attribute}]
105
106
  self.#{encrypted_attribute_name} = yate_encrypt(value, options[:key])
106
- self.yate_checksums[:#{attribute}] = yate_field_hash_value(:#{attribute})
107
+ self.yate_checksums[:#{attribute}] = yate_attribute_hash_value(:#{attribute})
107
108
  self.yate_dirty[:#{attribute}] = true
108
109
  end
109
110
  XXX
@@ -123,44 +124,6 @@ module YattrEncrypted
123
124
  end
124
125
  end
125
126
 
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
127
  # protected methods - nobody needs to use these outside of the model
165
128
  protected
166
129
 
@@ -184,7 +147,7 @@ module YattrEncrypted
184
147
  cipher = OpenSSL::Cipher.new(ALGORITHM)
185
148
  cipher.encrypt
186
149
  cipher.key = key
187
- iv = cipher.random_iv
150
+ iv = cipher.random_iv # ask OpenSSL for a new, random initial value
188
151
 
189
152
  # jsonify data
190
153
  value_marshalled = Marshal.dump value
@@ -220,19 +183,20 @@ module YattrEncrypted
220
183
  end
221
184
 
222
185
  # support for fields which are not atomic values
223
- def yate_field_hash_value(attribute)
186
+ def yate_attribute_hash_value(attribute)
224
187
  attribute = attribute.to_s if Symbol === attribute
225
188
  OpenSSL::HMAC.digest('md5', 'ersatz key', Marshal.dump(self.instance_variable_get("@#{attribute}")))
226
189
  end
227
190
 
228
- def yate_field_changed?(attribute)
191
+ def yate_attribute_changed?(attribute)
229
192
  attribute = attribute.to_sym unless Symbol === attribute
230
- yate_field_hash_value(attribute) != self.yate_checksums[attribute] || self.yate_dirty[attribute]
193
+ yate_attribute_hash_value(attribute) != self.yate_checksums[attribute] || self.yate_dirty[attribute]
231
194
  end
232
195
 
233
196
  def yate_update_encrypted_values
197
+ return unless yate_encrypted_attributes
234
198
  yate_encrypted_attributes.each do |attribute, options|
235
- if yate_field_changed?(attribute)
199
+ if yate_attribute_changed?(attribute)
236
200
  self.send "#{options[:attribute]}=".to_sym, yate_encrypt(self.send(attribute), options[:key])
237
201
  yate_dirty.delete(attribute)
238
202
  end
@@ -10,6 +10,11 @@ module ActiveRecord
10
10
  def self.attribute_methods_generated?
11
11
  true
12
12
  end
13
+
14
+ def self.before_save *methods
15
+ @before_save_hooks ||= []
16
+ @before_save_hooks += methods
17
+ end
13
18
 
14
19
  def save
15
20
  true
@@ -39,6 +44,11 @@ class TestYattrEncrypted < MiniTest::Unit::TestCase
39
44
  @sc = SomeClass.new
40
45
  end
41
46
 
47
+ def test_before_save_hook
48
+ assert SomeClass.instance_variable_get(:@before_save_hooks).include?(:yate_update_encrypted_values), \
49
+ "before_save_hooks should include :yate_encrypted_attributes"
50
+ end
51
+
42
52
  def test_yattr_encrypted_should_create_accessors
43
53
  assert @sc.respond_to?(:field), "a SomeClass instance should respond to :field"
44
54
  assert @sc.respond_to?(:field=), "a SomeClass instance should respond to :field="
@@ -46,7 +56,7 @@ class TestYattrEncrypted < MiniTest::Unit::TestCase
46
56
  assert @sc.respond_to?(:yate_encrypted_attributes), "a SomeClass instance should respond to :yate_encrypted_attributes"
47
57
  end
48
58
 
49
- def test_assigning_field_should_assign_field_encrypted
59
+ def test_assigning_attribute_should_assign_attribute_encrypted
50
60
  assert_nil @sc.field_encrypted, "field_encrypted should be nil prior to assignment to field"
51
61
  @sc.field = 'a field value'
52
62
  refute_nil @sc.field_encrypted, "field_encrypted should not be nil"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yattr_encrypted
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-18 00:00:00.000000000Z
12
+ date: 2012-03-19 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pry
16
- requirement: &2151817020 !ruby/object:Gem::Requirement
16
+ requirement: &2151767260 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2151817020
24
+ version_requirements: *2151767260
25
25
  description: Generates yattr_accessors that encrypt and decrypt attributes transparently.
26
26
  Based on attr_encrypted by Sean Huber [https://github.com/shuber]
27
27
  email: mike@clove.com
@@ -56,7 +56,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
56
56
  version: '0'
57
57
  segments:
58
58
  - 0
59
- hash: -1650387470150173914
59
+ hash: -3636252519896888946
60
60
  required_rubygems_version: !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements: