diff --git a/CHANGELOG.md b/CHANGELOG.md index f3bf2b1a..b1f7bffd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 1.2.0 (unreleased) - Made it easier to rotate master key +- Added `associated_data` option for database fields and files - Added `decimal` type - Added `encode_attributes` option - Fixed deprecation warnings with Rails 7.1 diff --git a/lib/lockbox/box.rb b/lib/lockbox/box.rb index dc580e2e..62e00c30 100644 --- a/lib/lockbox/box.rb +++ b/lib/lockbox/box.rb @@ -1,6 +1,8 @@ module Lockbox class Box - def initialize(key: nil, algorithm: nil, encryption_key: nil, decryption_key: nil, padding: false) + NOT_SET = Object.new + + def initialize(key: nil, algorithm: nil, encryption_key: nil, decryption_key: nil, padding: false, associated_data: nil) raise ArgumentError, "Cannot pass both key and encryption/decryption key" if key && (encryption_key || decryption_key) key = Lockbox::Utils.decode_key(key) if key @@ -32,9 +34,11 @@ def initialize(key: nil, algorithm: nil, encryption_key: nil, decryption_key: ni @algorithm = algorithm @padding = padding == true ? 16 : padding + @associated_data = associated_data end - def encrypt(message, associated_data: nil) + def encrypt(message, associated_data: NOT_SET) + associated_data = @associated_data if associated_data == NOT_SET message = Lockbox.pad(message, size: @padding) if @padding case @algorithm when "hybrid" @@ -53,7 +57,8 @@ def encrypt(message, associated_data: nil) nonce + ciphertext end - def decrypt(ciphertext, associated_data: nil) + def decrypt(ciphertext, associated_data: NOT_SET) + associated_data = @associated_data if associated_data == NOT_SET message = case @algorithm when "hybrid" diff --git a/test/internal/db/schema.rb b/test/internal/db/schema.rb index beff8ad1..c4d53fcc 100644 --- a/test/internal/db/schema.rb +++ b/test/internal/db/schema.rb @@ -88,6 +88,7 @@ t.text :conf_ciphertext t.text :city_ciphertext t.binary :ssn_ciphertext + t.text :region_ciphertext t.text :state t.text :state_ciphertext t.text :photo_data diff --git a/test/model_test.rb b/test/model_test.rb index 1a5714f2..c1433f9a 100644 --- a/test/model_test.rb +++ b/test/model_test.rb @@ -537,6 +537,15 @@ def test_encode assert_equal nonce_size + ssn.bytesize + auth_tag_size, user.ssn_ciphertext.bytesize end + def test_associated_data + user = User.create!(name: "Test", region: "Data") + assert_equal "Data", User.last.region + user.update!(name: "New") + assert_raises(Lockbox::DecryptionError) do + User.last.region + end + end + def test_attribute_key_encrypted_column email = "test@example.org" user = User.create!(email: email) diff --git a/test/support/active_record.rb b/test/support/active_record.rb index 6e534b72..cc111b67 100644 --- a/test/support/active_record.rb +++ b/test/support/active_record.rb @@ -105,6 +105,7 @@ def deserialize(value) has_encrypted :city, padding: true has_encrypted :ssn, encode: false + has_encrypted :region, associated_data: -> { name } has_encrypted :state diff --git a/test/support/mongoid.rb b/test/support/mongoid.rb index 933b9783..33b57cd0 100644 --- a/test/support/mongoid.rb +++ b/test/support/mongoid.rb @@ -13,6 +13,7 @@ class User field :phone_ciphertext, type: String field :city_ciphertext, type: String field :ssn_ciphertext, type: BSON::Binary + field :region_ciphertext, type: String field :state, type: String field :state_ciphertext, type: String @@ -23,6 +24,7 @@ class User has_encrypted :city, padding: true has_encrypted :ssn, encode: false + has_encrypted :region, associated_data: -> { name } has_encrypted :state include PhotoUploader::Attachment(:photo)