Skip to content

Commit

Permalink
Merge pull request #204 from saghaulor/v3
Browse files Browse the repository at this point in the history
V3
  • Loading branch information
saghaulor committed Mar 30, 2016
2 parents 53f6ec0 + 715b0cc commit 3d79f68
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 65 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# attr_encrypted #

## 3.0.0 ##
* Changed: Updated gemspec to use Encryptor v3.0.0. (@saghaulor)
* Changed: Updated README with instructions related to moving from v2.0.0 to v3.0.0. (@saghaulor)
* Fixed: ActiveModel::Dirty methods in the ActiveRecord adapter. (@saghaulor)

## 2.0.0 ##
* Added: Now using Encryptor v2.0.0 (@saghaulor)
* Added: Options are copied to the instance. (@saghaulor)
Expand Down
40 changes: 32 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ It works with ANY class, however, you get a few extra features when you're using
Add attr_encrypted to your gemfile:

```ruby
gem "attr_encrypted", "~> 2.0.0"
gem "attr_encrypted", "~> 3.0.0"
```

Then install the gem:
Expand All @@ -37,22 +37,22 @@ If you're using a PORO, you have to do a little bit more work by extending the c
extend AttrEncrypted
attr_accessor :name
attr_encrypted :ssn, key: 'This is a key that is 256 bits!!'

def load
# loads the stored data
end

def save
# saves the :name and :encrypted_ssn attributes somewhere (e.g. filesystem, database, etc)
end
end

user = User.new
user.ssn = '123-45-6789'
user.ssn # returns the unencrypted object ie. '123-45-6789'
user.encrypted_ssn # returns the encrypted version of :ssn
user.save

user = User.load
user.ssn # decrypts :encrypted_ssn and returns '123-45-6789'
```
Expand Down Expand Up @@ -242,7 +242,7 @@ Lets suppose you'd like to use this custom encryptor class:
def self.silly_encrypt(options)
(options[:value] + options[:secret_key]).reverse
end

def self.silly_decrypt(options)
options[:value].reverse.gsub(/#{options[:secret_key]}$/, '')
end
Expand Down Expand Up @@ -374,12 +374,12 @@ Backwards compatibility is supported by providing a special option that is passe
The `:insecure_mode` option will allow encryptor to ignore the new security requirements. It is strongly advised that if you use this older insecure behavior that you migrate to the newer more secure behavior.


## Upgrading from attr_encrypted v1.x to v2.x
## Upgrading from attr_encrypted v1.x to v3.x

Modify your gemfile to include the new version of attr_encrypted:

```ruby
gem attr_encrypted, "~> 2.0.0"
gem attr_encrypted, "~> 3.0.0"
```

The update attr_encrypted:
Expand All @@ -390,6 +390,30 @@ The update attr_encrypted:

Then modify your models using attr\_encrypted to account for the changes in default options. Specifically, pass in the `:mode` and `:algorithm` options that you were using if you had not previously done so. If your key is insufficient length relative to the algorithm that you use, you should also pass in `insecure_mode: true`; this will prevent Encryptor from raising an exception regarding insufficient key length. Please see the Deprecations sections for more details including an example of how to specify your model with default options from attr_encrypted v1.x.

## Upgrading from attr_encrypted v2.x to v3.x

A bug was discovered in Encryptor v2.0.0 that inccorectly set the IV when using an AES-\*-GCM algorithm. Unfornately fixing this major security issue results in the inability to decrypt records encrypted using an AES-*-GCM algorithm from Encryptor v2.0.0. Please see [Upgrading to Encryptor v3.0.0](https://github.com/attr-encrypted/encryptor#upgrading-from-v200-to-v300) for more info.

It is strongly advised that you re-encrypt your data encrypted with Encryptor v2.0.0. However, you'll have to take special care to re-encrypt. To decrypt data encrypted with Encryptor v2.0.0 using an AES-\*-GCM algorithm you can use the `:v2_gcm_iv` option.

It is recommended that you implement a strategy to insure that you do not mix the encryption implementations of Encryptor. One way to do this is to re-encrypt everything while your application is offline.Another way is to add a column that keeps track of what implementation was used. The path that you choose will depend on your situtation. Below is an example of how you might go about re-encrypting your data.

```ruby
class User
attr_encrypted :ssn, key: :encryption_key, v2_gcm_iv: :is_decrypting?(:ssn)

def is_decrypting?(attribute)
encrypted_atributes[attribute][operation] == :decrypting
end
end

User.all.each do |user|
old_ssn = user.ssn
user.ssn= old_ssn
user.save
end
```

## Things to consider before using attr_encrypted

#### Searching, joining, etc
Expand Down
7 changes: 5 additions & 2 deletions attr_encrypted.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Gem::Specification.new do |s|

s.required_ruby_version = '>= 2.0.0'

s.add_dependency('encryptor', ['~> 2.0.0'])
s.add_dependency('encryptor', ['~> 3.0.0'])
# support for testing with specific active record version
activerecord_version = if ENV.key?('ACTIVERECORD')
"~> #{ENV['ACTIVERECORD']}"
Expand All @@ -55,6 +55,9 @@ Gem::Specification.new do |s|
s.cert_chain = ['certs/saghaulor.pem']
s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/

s.post_install_message = "\n\n\nWARNING: Several insecure default options and features have been deprecated in attr_encrypted v2.0.0. Please see the README for more details.\n\n\n"
s.post_install_message = "\n\n\nWARNING: Several insecure default options and features were deprecated in attr_encrypted v2.0.0.\n
Additionally, there was a bug in Encryptor v2.0.0 that insecurely encrypted data when using an AES-*-GCM algorithm.\n
This bug was fixed but introduced breaking changes between v2.x and v3.x.\n
Please see the README for more information regarding upgrading to attr_encrypted v3.0.0.\n\n\n"

end
6 changes: 3 additions & 3 deletions lib/attr_encrypted.rb
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ def encrypted_attributes
def evaluated_attr_encrypted_options_for(attribute)
evaluated_options = Hash.new
attribute_option_value = encrypted_attributes[attribute.to_sym][:attribute]
self.class.encrypted_attributes[attribute.to_sym].map do |option, value|
encrypted_attributes[attribute.to_sym].map do |option, value|
evaluated_options[option] = evaluate_attr_encrypted_option(value)
end

Expand Down Expand Up @@ -383,7 +383,7 @@ def evaluate_attr_encrypted_option(option)
def load_iv_for_attribute(attribute, options)
encrypted_attribute_name = options[:attribute]
encode_iv = options[:encode_iv]
iv = send("#{encrypted_attribute_name}_iv")
iv = options[:iv] || send("#{encrypted_attribute_name}_iv")
if options[:operation] == :encrypting
begin
iv = generate_iv(options[:algorithm])
Expand All @@ -407,7 +407,7 @@ def generate_iv(algorithm)
def load_salt_for_attribute(attribute, options)
encrypted_attribute_name = options[:attribute]
encode_salt = options[:encode_salt]
salt = send("#{encrypted_attribute_name}_salt")
salt = options[:salt] || send("#{encrypted_attribute_name}_salt")
if (salt == nil)
salt = SecureRandom.random_bytes
salt = prefix_and_encode_salt(salt, encode_salt) if encode_salt
Expand Down
10 changes: 8 additions & 2 deletions lib/attr_encrypted/adapters/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,17 @@ def attr_encrypted(*attrs)
options.merge! encrypted_attributes[attr]

define_method("#{attr}_changed?") do
send(attr) != decrypt(attr, send("#{options[:attribute]}_was"))
if send("#{options[:attribute]}_changed?")
send(attr) != send("#{attr}_was")
end
end

define_method("#{attr}_was") do
decrypt(attr, send("#{options[:attribute]}_was")) if send("#{attr}_changed?")
iv_and_salt = { iv: send("#{options[:attribute]}_iv_was"), salt: send("#{options[:attribute]}_salt_was"), operation: :decrypting }
encrypted_attributes[attr].merge!(iv_and_salt)
evaluated_options = evaluated_attr_encrypted_options_for(attr)
[:iv, :salt, :operation].each { |key| encrypted_attributes[attr].delete(key) }
self.class.decrypt(attr, send("#{options[:attribute]}_was"), evaluated_options)
end

alias_method "#{attr}_before_type_cast", attr
Expand Down
2 changes: 1 addition & 1 deletion lib/attr_encrypted/version.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module AttrEncrypted
# Contains information about this gem's version
module Version
MAJOR = 2
MAJOR = 3
MINOR = 0
PATCH = 0

Expand Down
Loading

0 comments on commit 3d79f68

Please sign in to comment.