Last Updated: February 25, 2016
·
619
· aceofspades

Store Decryptable Passwords in ActiveModel Instances

This is great when you need two-way encryption to store external service passwords.

gem 'gibberish'

module HasDecryptablePassword
  SALT_LENGTH = 64
  KEY_LENGTH = 64

  # TODO move (and change) AES_KEY to secrets.yml
  # AES_KEY will need to be known by all apps, i.e background jobs, etc.
  AES_KEY = 'your-secret-key'
  AES_SIZE = 256 # TODO 512 is not supported by gibberish gem, investigate

  module ActiveModel
    extend ActiveSupport::Concern

    included do
      include HasDecryptablePassword
      validates :encrypted_password, presence: true
      validates :salt, presence: true, length: {is: SALT_LENGTH}
      before_validation :encrypt_password
    end
  end

  def encrypt_password
    self.encrypted_password = aes_cipher.enc(password)
    true
  end

  def decrypted_password
    aes_cipher.dec(encrypted_password)
  end

  private

  def aes_cipher
    Gibberish::AES.new(key, AES_SIZE)
  end

  def key
    generate_salt
    fail unless AES_KEY.length == KEY_LENGTH
    fail unless salt.length == SALT_LENGTH
    AES_KEY + salt
  end

  def generate_salt
    self.salt ||= SecureRandom.hex(SALT_LENGTH >> 1)
  end

end