README.md in lockbox-0.1.0 vs README.md in lockbox-0.1.1

- old
+ new

@@ -1,30 +1,34 @@ # Lockbox :lock: File encryption for Ruby and Rails -Supports Active Storage and CarrierWave +- Supports Active Storage and CarrierWave +- Uses AES-GCM by default for [authenticated encryption](https://tonyarcieri.com/all-the-crypto-code-youve-ever-written-is-probably-broken) +- Makes key rotation easy -Uses AES-GCM by default for [authenticated encryption](https://tonyarcieri.com/all-the-crypto-code-youve-ever-written-is-probably-broken) +Check out [this post](https://ankane.org/sensitive-data-rails) for more info on securing sensitive data with Rails +[![Build Status](https://travis-ci.org/ankane/lockbox.svg?branch=master)](https://travis-ci.org/ankane/lockbox) + ## Installation Add this line to your application’s Gemfile: ```ruby gem 'lockbox' ``` ## Key Generation -Generate an encryption key. +Generate an encryption key ```ruby SecureRandom.hex(32) ``` -Store the key with your other secrets (typically Rails secrets or an environment variable). +Store the key with your other secrets. This is typically Rails credentials or an environment variable ([dotenv](https://github.com/bkeepers/dotenv) is great for this). Be sure to use different keys in development and production. Keys don’t need to be hex-encoded, but it’s often easier to store them this way. Alternatively, you can use a [key management service](#key-management) to manage your keys. ## Files @@ -35,17 +39,17 @@ ``` Encrypt ```ruby -box.encrypt(File.binread("license.jpg")) +ciphertext = box.encrypt(File.binread("license.jpg")) ``` Decrypt ```ruby -box.decrypt(File.binread("license.jpg.enc")) +box.decrypt(ciphertext) ``` ## Active Storage Add to your model: @@ -133,15 +137,15 @@ ## Algorithms ### AES-GCM -The default algorithm is AES-GCM with a 256-bit key. Rotate the key every 2 billion files to minimize the chance of a [nonce collision](https://www.cryptologie.net/article/402/is-symmetric-security-solved/). +The default algorithm is AES-GCM with a 256-bit key. Rotate the key every 2 billion files to minimize the chance of a [nonce collision](https://www.cryptologie.net/article/402/is-symmetric-security-solved/), which will leak the key. ### XChaCha20 -[Install Libsodium](https://github.com/crypto-rb/rbnacl/wiki/Installing-libsodium) and add [rbnacl](https://github.com/crypto-rb/rbnacl) to your application’s Gemfile: +[Install Libsodium](https://github.com/crypto-rb/rbnacl/wiki/Installing-libsodium) >= 1.0.12 and add [rbnacl](https://github.com/crypto-rb/rbnacl) to your application’s Gemfile: ```ruby gem 'rbnacl' ``` @@ -168,10 +172,47 @@ Lockbox.default_options = {algorithm: "xchacha20"} ``` You can also pass an algorithm to `previous_versions` for key rotation. +## Hybrid Cryptography + +[Hybrid cryptography](https://en.wikipedia.org/wiki/Hybrid_cryptosystem) allows servers to encrypt data without being able to decrypt it. + +[Install Libsodium](https://github.com/crypto-rb/rbnacl/wiki/Installing-libsodium) and add [rbnacl](https://github.com/crypto-rb/rbnacl) to your application’s Gemfile: + +```ruby +gem 'rbnacl' +``` + +Generate a key pair with: + +```ruby +Lockbox.generate_key_pair +``` + +Store the keys with your other secrets. Then use: + +```ruby +# files +box = Lockbox.new(algorithm: "hybrid", encryption_key: encryption_key, decryption_key: decryption_key) + +# Active Storage +class User < ApplicationRecord + attached_encrypted :license, algorithm: "hybrid", encryption_key: encryption_key, decryption_key: decryption_key +end + +# CarrierWave +class LicenseUploader < CarrierWave::Uploader::Base + encrypt algorithm: "hybrid", encryption_key: encryption_key, decryption_key: decryption_key +end +``` + +Make sure `decryption_key` is `nil` on servers that shouldn’t decrypt. + +This uses X25519 for key exchange and XSalsa20-Poly1305 for encryption. + ## Key Management You can use a key management service to manage your keys with [KMS Encrypted](https://github.com/ankane/kms_encrypted). For Active Storage, use: @@ -189,9 +230,68 @@ encrypt key: -> { model.kms_key } end ``` **Note:** KMS Encrypted’s key rotation does not know to rotate encrypted files, so avoid calling `record.rotate_kms_key!` on models with file uploads for now. + +## Compatibility + +It’s easy to read encrypted files in another language if needed. + +Here are [some examples](docs/Compatibility.md). + +The format for AES-GCM is: + +- nonce (IV) - 12 bytes +- ciphertext - variable length +- authentication tag - 16 bytes + +For XChaCha20, use the appropriate [Libsodium library](https://libsodium.gitbook.io/doc/bindings_for_other_languages). + +## Database Fields + +Lockbox can also be used with [attr_encrypted](https://github.com/attr-encrypted/attr_encrypted) for database fields. This gives you: + +1. Easy key rotation +2. XChaCha20 +3. Hybrid cryptography +4. No need for separate IV columns + +Add to your Gemfile: + +```ruby +gem 'attr_encrypted' +``` + +Create a migration to add a new column for the encrypted data. We don’t need a separate IV column, as this will be included in the encrypted data. + +```ruby +class AddEncryptedPhoneToUsers < ActiveRecord::Migration[5.2] + def change + add_column :users, :encrypted_phone, :string + end +end +``` + +All Lockbox options are supported. + +```ruby +class User < ApplicationRecord + attr_encrypted :phone, encryptor: Lockbox::Encryptor, key: key, algorithm: "xchacha20", previous_versions: [{key: previous_key}] + + attribute :encrypted_phone_iv # prevent attr_encrypted error +end +``` + +For hybrid cryptography, use: + +```ruby +class User < ApplicationRecord + attr_encrypted :phone, encryptor: Lockbox::Encryptor, algorithm: "hybrid", encryption_key: encryption_key, decryption_key: decryption_key + + attribute :encrypted_phone_iv # prevent attr_encrypted error +end +``` ## Reference Pass associated data to encryption and decryption