Skip to content

Latest commit

 

History

History
265 lines (208 loc) · 7.17 KB

README.adoc

File metadata and controls

265 lines (208 loc) · 7.17 KB

Transcryptor

Build Status Code Climate Quality Code Climate Quality

Transcryptor provides utility functions to help migrate records encrypted with attr_encrypted from one encryption configuration to another.

Installation

Add this line to your application’s Gemfile:

gem 'transcryptor', github: 'riboseinc/transcryptor'

And then execute:

bundle

Or install it yourself as:

gem install transcryptor

Usage

ActiveRecord::Migration

You have a User with ssn attribute which needs to be re-encrypted. User has next configuration:

class User < ActiveRecord::Base
  attr_encrypted :ssn, key: ->(u) { ENV['USER_SSN_ENC_KEY'] },
                       mode: :per_attribute_iv_and_salt,
                       algorithm: 'aes-256-gcm'
end

To re-ecrypt this column with new key (ENV['NEW_USER_SSN_ENC_KEY']), algorithm (aes-256-cbc) and mode (per_attribute_iv) you can easily define migration.

class ReEncryptUserSsn < ActiveRecord::Migration
  def up
    re_encrypt_column :users, :ssn,
      { # old configuration of attr_encrypted for :ssn column
        key: ->(u) { ENV['USER_SSN_ENC_KEY'] },
        mode: :per_attribute_iv_and_salt,
        algorithm: 'aes-256-gcm'
      },
      { # new configuration of attr_encrypted for :ssn column
        key: ->(u) { ENV['NEW_USER_SSN_ENC_KEY'] },
        mode: :per_attribute_iv,
        algorithm: 'aes-256-cbc'
      }
  end
end

Run bundle exec rake db:migrate. Done!

DataMapper::Migration

See ActiveRecord::Migration for initial data. And then DataMapper migration is:

require 'dm-migrations/migration_runner'

migration 1, :re_encrypt_user_ssn do
  up do
    re_encrypt_column :users, :ssn,
      { # old configuration of attr_encrypted for :ssn column
        key: ->(u) { ENV['USER_SSN_ENC_KEY'] },
        mode: :per_attribute_iv_and_salt,
        algorithm: 'aes-256-gcm'
      },
      { # new configuration of attr_encrypted for :ssn column
        key: ->(u) { ENV['NEW_USER_SSN_ENC_KEY'] },
        mode: :per_attribute_iv,
        algorithm: 'aes-256-cbc'
      }
  end
end

migrate_up!

Additional options

Taking ActiveRecord as an example (the following applies to DataMapper as well):

where

Specify an SQL string (or a proc returning one) inside the where option to limit the range on which transcryption is run:

class ReEncryptUserSsn < ActiveRecord::Migration
  def up
    re_encrypt_column :users, :ssn,
      { # old configuration of attr_encrypted for :ssn column
        key: ->(u) { ENV['USER_SSN_ENC_KEY'] },
        mode: :per_attribute_iv_and_salt,
        algorithm: 'aes-256-gcm'
      },
      { # new configuration of attr_encrypted for :ssn column
        key: ->(u) { ENV['NEW_USER_SSN_ENC_KEY'] },
        mode: :per_attribute_iv,
        algorithm: 'aes-256-cbc'
      },
      where: -> { "encrypted_ssn IS NOT NULL" }
  end
end

Zero Downtime Migration

ActiveRecord

Create new columns in database.

class Migration1 < ActiveRecord::Migration
  def up
    add_column :users, :encrypted_new_ssn
    add_column :users, :encrypted_new_ssn_iv
  end
end

Add attr_encrypted for new columns, include Transcryptor::ActiveRecord::ZeroDowntime, and transcryptor_migrate :old_attribute_name, :new_attribute_name.

class User < ActiveRecord::Base
  include Transcryptor::ActiveRecord::ZeroDowntime

  attr_encrypted :ssn, key: '1qwe1qwe1qwe1qwe1qwe1qwe1qwe1qwe', algorithm: 'aes-256-cbc'
  attr_encrypted :new_ssn, key: '2asd2asd2asd2asd2asd2asd2asd2asd', algorithm: 'aes-256-gcm'

  transcryptor_migrate :ssn, :new_ssn
end

Create rake task for zero downtime migration. Or any other way you prefer.

namespace :zero_downtime
  desc 'migrate attr_encrypted for User'
  task user: :environment do
    User.find_each { |user| user.save! }
  end
end

Remove & rename columns in database after finishing of rake task.

class Migration2 < ActiveRecord::Migration
  def up
    remove_column :users, :encrypted_ssn
    remove_column :users, :encrypted_ssn_iv

    rename_column :users, :encrypted_new_ssn, :encrypted_ssn
    rename_column :users, :encrypted_new_ssn_iv, :encrypted_ssn_iv
  end
end

Move attr_encrypted configuration to original attribute and remove all migration code.

class User < ActiveRecord::Base
  attr_encrypted :ssn, key: '2asd2asd2asd2asd2asd2asd2asd2asd', algorithm: 'aes-256-gcm'
end

Done!

Default Options

Default options for old and new configuration are absolutelly the same as it is defined in attr_encrypted gem.

{
  prefix:            'encrypted_',
  suffix:            '',
  if:                true,
  unless:            false,
  encode:            true, # changed from false to true as transcryptor works with DB rows
  encode_iv:         true,
  encode_salt:       true,
  default_encoding:  'm',
  marshal:           false,
  marshaler:         Marshal,
  dump_method:       'dump',
  load_method:       'load',
  encryptor:         Encryptor,
  encrypt_method:    'encrypt',
  decrypt_method:    'decrypt',
  mode:              :per_attribute_iv,
  algorithm:         'aes-256-gcm',
}

Zero downtime migration using VersionedFields (from version 0.3.0)

You can use transcryptor to migrate your encrypted fields in zero downtime using versioned_fields gem.

Just include Transcryptor::Decoder module and use decode_with_previous_settings to decode your fields with specific settings:

# db/migrate_versioned_fields/user/ssn.rb

VersionedFields::Migration.draw_for(User, :ssn) do
  config.include Transcryptor::Decoder

  version 1
  version(2) do
    decode_with_previous_settings(migration,
      key:       '67c3800d1572d9d964a6ff3bd821ed02',
      algorithm: 'aes-256-gcm'
    )
  end

  version(3) do |migration|
    decode_with_previous_settings(migration,
      key:       '94dd7e2c40a3d51a8dd0a9137356a18e',
      algorithm: 'RC2-64-CBC'
    )
end

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/riboseinc/transcryptor. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.