attr_vault 0.0.4 → 0.0.6

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 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