attr_vault 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 18e01d8e9bbd38cbd41cce47d4c7dd5650ec976f
4
- data.tar.gz: ff870a7ba7ec9eacc172b21bf33db9ffb0644af1
3
+ metadata.gz: 51257ff03a337f75042850857d0f468a7b55b6c7
4
+ data.tar.gz: d57fad1f79b934d11ff0e087e80b0e6acc04ea27
5
5
  SHA512:
6
- metadata.gz: 1251126d7b737797573a7450f519c3eaf7c381830c95585dfe630652b5b9a37c1fd154e5e2f7611dcc2f647fe646a27401ca837cda225d4220b2355b9309d2bd
7
- data.tar.gz: e9fcc3d3fb1706049626c493f40ea4be889f1acbec73fc3ac832083f3ff1dd86a4ecfa3748bb8ceea1918b7568d61d5ecef0c6f26dff139db8b1bcbc33f53060
6
+ metadata.gz: d5e950284be5139fc7596f8eb6ace59132eaa2046245079fa5d421a0f41ae668a984dd546f4ced3004a28142af4fb93cb9596b7a02c6d57d4cbd1a7d72e1e722
7
+ data.tar.gz: 49c6168c1f49ce9ec1a78f596db759beb8b65aeb967a6853154e6c1fd65684972767da413b1bb0dc12d176e013506c887cc6eaef52a218d09cebcee404defff1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- attr_vault (0.0.3)
4
+ attr_vault (0.0.6)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -1,8 +1,7 @@
1
1
  module AttrVault
2
2
  module Cryptor
3
3
  def self.encrypt(value, key)
4
- return [nil, nil] if value.nil?
5
- return ['', ''] if value.empty?
4
+ return value if value.nil? || value.empty?
6
5
 
7
6
  secret = AttrVault::Secret.new(key)
8
7
 
@@ -13,21 +12,22 @@ module AttrVault
13
12
 
14
13
  encrypted_payload = iv + encrypted_message
15
14
  mac = OpenSSL::HMAC.digest('sha256', secret.signing_key, encrypted_payload)
16
- [ encrypted_payload, mac ]
15
+ Sequel.blob(mac + encrypted_payload)
17
16
  end
18
17
 
19
- def self.decrypt(encrypted_payload, hmac, key)
20
- return nil if encrypted_payload.nil? && hmac.nil?
21
- return '' if encrypted_payload.empty? && hmac.empty?
18
+ def self.decrypt(encrypted, key)
19
+ return encrypted if encrypted.nil? || encrypted.empty?
22
20
 
23
21
  secret = AttrVault::Secret.new(key)
24
22
 
23
+ hmac, encrypted_payload = encrypted[0...32], encrypted[32..-1]
24
+
25
25
  expected_hmac = Encryption.hmac_digest(secret.signing_key, encrypted_payload)
26
26
  unless verify_signature(expected_hmac, hmac)
27
27
  raise InvalidCiphertext, "Expected hmac #{expected_hmac} for this value; got #{hmac}"
28
28
  end
29
29
 
30
- iv, encrypted_message = encrypted_payload[0..16], encrypted_payload[16..-1]
30
+ iv, encrypted_message = encrypted_payload[0...16], encrypted_payload[16..-1]
31
31
 
32
32
  block_size = Encryption::AES_BLOCK_SIZE
33
33
  unless (encrypted_message.size % block_size).zero?
@@ -1,3 +1,3 @@
1
1
  module AttrVault
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.6"
3
3
  end
data/lib/attr_vault.rb CHANGED
@@ -3,6 +3,7 @@ require 'attr_vault/keyring'
3
3
  require 'attr_vault/secret'
4
4
  require 'attr_vault/encryption'
5
5
  require 'attr_vault/cryptor'
6
+ require 'digest/sha1'
6
7
 
7
8
  module AttrVault
8
9
  def self.included(base)
@@ -44,17 +45,16 @@ module AttrVault
44
45
  next unless @vault_dirty_attrs.has_key? attr.name
45
46
 
46
47
  value = @vault_dirty_attrs[attr.name]
47
- encrypted, hmac = Cryptor.encrypt(value, current_key.value)
48
-
49
- unless encrypted.nil?
50
- encrypted = Sequel.blob(encrypted)
51
- end
52
- unless hmac.nil?
53
- hmac = Sequel.blob(hmac)
54
- end
48
+ encrypted = Cryptor.encrypt(value, current_key.value)
55
49
 
56
50
  self[attr.encrypted_field] = encrypted
57
- self[attr.hmac_field] = hmac
51
+ unless attr.digest_field.nil?
52
+ if value.nil?
53
+ self[attr.digest_field] = nil
54
+ else
55
+ self[attr.digest_field] = Digest::SHA1.hexdigest(value)
56
+ end
57
+ end
58
58
  end
59
59
  self[self.class.vault_key_field] = current_key.id
60
60
  @vault_dirty_attrs = {}
@@ -73,6 +73,7 @@ module AttrVault
73
73
  self.vault_attrs << attr
74
74
 
75
75
  define_method(name) do
76
+ @vault_dirty_attrs ||= {}
76
77
  if @vault_dirty_attrs.has_key? name
77
78
  return @vault_dirty_attrs[name]
78
79
  end
@@ -92,9 +93,8 @@ module AttrVault
92
93
  record_key = self.class.vault_keys.fetch(key_id)
93
94
 
94
95
  encrypted_value = self[attr.encrypted_field]
95
- hmac = self[attr.hmac_field]
96
96
  # TODO: cache decrypted value
97
- Cryptor.decrypt(encrypted_value, hmac, record_key.value)
97
+ Cryptor.decrypt(encrypted_value, record_key.value)
98
98
  end
99
99
 
100
100
  define_method("#{name}=") do |value|
@@ -104,7 +104,9 @@ module AttrVault
104
104
  # be updated--otherwise, the object is never saved,
105
105
  # #before_save is never called, and we never store the update
106
106
  self.modified! attr.encrypted_field
107
- self.modified! attr.hmac_field
107
+ unless attr.digest_field.nil?
108
+ self.modified! attr.digest_field
109
+ end
108
110
  end
109
111
  end
110
112
 
@@ -122,16 +124,16 @@ module AttrVault
122
124
  end
123
125
 
124
126
  class VaultAttr
125
- attr_reader :name, :encrypted_field, :hmac_field, :plaintext_source_field
127
+ attr_reader :name, :encrypted_field, :plaintext_source_field, :digest_field
126
128
 
127
129
  def initialize(name,
128
130
  encrypted_field: "#{name}_encrypted",
129
- hmac_field: "#{name}_hmac",
130
- plaintext_source_field: nil)
131
+ plaintext_source_field: nil,
132
+ digest_field: nil)
131
133
  @name = name
132
134
  @encrypted_field = encrypted_field.to_sym
133
- @hmac_field = hmac_field.to_sym
134
135
  @plaintext_source_field = plaintext_source_field.to_sym unless plaintext_source_field.nil?
136
+ @digest_field = digest_field.to_sym unless digest_field.nil?
135
137
  end
136
138
  end
137
139
  end
@@ -205,7 +205,6 @@ describe AttrVault do
205
205
  expect(record.secret).to eq secret1
206
206
 
207
207
  old_secret_encrypted = record.secret_encrypted
208
- old_secret_hmac = record.secret_hmac
209
208
 
210
209
  new_key_record = item2[record.id]
211
210
  new_key_record.update(secret: secret2)
@@ -214,7 +213,6 @@ describe AttrVault do
214
213
  expect(new_key_record.key_id).to eq key2_id
215
214
  expect(new_key_record.secret).to eq secret2
216
215
  expect(new_key_record.secret_encrypted).not_to eq old_secret_encrypted
217
- expect(new_key_record.secret_hmac).not_to eq old_secret_hmac
218
216
  end
219
217
 
220
218
  it "rewrites the items using the current key even if they are not updated" do
@@ -225,7 +223,6 @@ describe AttrVault do
225
223
  expect(record.secret).to eq secret1
226
224
 
227
225
  old_secret_encrypted = record.secret_encrypted
228
- old_secret_hmac = record.secret_hmac
229
226
 
230
227
  new_key_record = item2[record.id]
231
228
  new_key_record.update(other: secret2)
@@ -234,7 +231,6 @@ describe AttrVault do
234
231
  expect(new_key_record.key_id).to eq key2_id
235
232
  expect(new_key_record.secret).to eq secret1
236
233
  expect(new_key_record.secret_encrypted).not_to eq old_secret_encrypted
237
- expect(new_key_record.secret_hmac).not_to eq old_secret_hmac
238
234
  expect(new_key_record.other).to eq secret2
239
235
  end
240
236
  end
@@ -301,14 +297,13 @@ describe AttrVault do
301
297
  created_at: Time.now }].to_json
302
298
  }
303
299
 
304
- it "supports renaming the encrypted and hmac fields" do
300
+ it "supports renaming the encrypted field" do
305
301
  k = key_data
306
302
  item = Class.new(Sequel::Model(:items)) do
307
303
  include AttrVault
308
304
  vault_keyring k
309
305
  vault_attr :classified_info,
310
- encrypted_field: :secret_encrypted,
311
- hmac_field: :secret_hmac
306
+ encrypted_field: :secret_encrypted
312
307
  end
313
308
 
314
309
  secret = "we've secretly replaced the fine coffee they usually serve with Folgers Crystals"
@@ -316,7 +311,6 @@ describe AttrVault do
316
311
  s.reload
317
312
  expect(s.classified_info).to eq secret
318
313
  expect(s.secret_encrypted).not_to eq secret
319
- expect(s.secret_hmac).not_to be_nil
320
314
  end
321
315
 
322
316
  it "supports renaming the key id field" do
@@ -332,9 +326,56 @@ describe AttrVault do
332
326
  s.reload
333
327
  expect(s.secret).to eq secret
334
328
  expect(s.secret_encrypted).not_to eq secret
335
- expect(s.secret_hmac).not_to be_nil
336
329
  expect(s.alt_key_id).not_to be_nil
337
330
  expect(s.key_id).to be_nil
338
331
  end
339
332
  end
333
+
334
+ context "with a digest field" do
335
+ let(:key_id) { '80a8571b-dc8a-44da-9b89-caee87e41ce2' }
336
+ let(:key_data) {
337
+ [{
338
+ id: key_id,
339
+ value: 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=',
340
+ created_at: Time.now }].to_json
341
+ }
342
+ let(:item) {
343
+ # the let form can't be evaluated inside the class definition
344
+ # because Ruby scoping rules were written by H.P. Lovecraft, so
345
+ # we create a local here to work around that
346
+ k = key_data
347
+ Class.new(Sequel::Model(:items)) do
348
+ include AttrVault
349
+ vault_keyring k
350
+ vault_attr :secret, digest_field: :secret_digest
351
+ vault_attr :other, digest_field: :other_digest
352
+ end
353
+ }
354
+
355
+ it "records the sha1 of the plaintext value" do
356
+ secret = 'snape kills dumbledore'
357
+ s = item.create(secret: secret)
358
+ expect(s.secret_digest).to eq(Digest::SHA1.hexdigest(secret))
359
+ end
360
+
361
+ it "can record multiple digest fields" do
362
+ secret = 'joffrey kills ned'
363
+ other_secret = '"gomer pyle" lawrence kills himself'
364
+ s = item.create(secret: secret, other: other_secret)
365
+ expect(s.secret_digest).to eq(Digest::SHA1.hexdigest(secret))
366
+ expect(s.other_digest).to eq(Digest::SHA1.hexdigest(other_secret))
367
+ end
368
+
369
+ it "records the digest for an empty field" do
370
+ s = item.create(secret: '', other: '')
371
+ expect(s.secret_digest).to eq(Digest::SHA1.hexdigest(''))
372
+ expect(s.other_digest).to eq(Digest::SHA1.hexdigest(''))
373
+ end
374
+
375
+ it "records the digest of a nil field" do
376
+ s = item.create
377
+ expect(s.secret_digest).to be_nil
378
+ expect(s.other_digest).to be_nil
379
+ end
380
+ end
340
381
  end
data/spec/spec_helper.rb CHANGED
@@ -20,9 +20,9 @@ conn.run <<-EOF
20
20
  key_id uuid,
21
21
  alt_key_id uuid,
22
22
  secret_encrypted bytea,
23
- secret_hmac bytea,
23
+ secret_digest text,
24
24
  other_encrypted bytea,
25
- other_hmac bytea,
25
+ other_digest text,
26
26
  not_secret text,
27
27
  other_not_secret text
28
28
  )
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attr_vault
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciek Sakrejda