Cloak

Cloak makes it easy to encrypt and decrypt database fields using Ecto.

This Cloak module is Cloak's main entry point. It wraps the encryption and decryption process, ensuring that everything works smoothly without downtime even when there are multiple encryption ciphers and keys in play at the same time.

Configuration

The actual encryption work is delegated to the cipher module that you specify in Cloak's configuration. Cipher modules must adhere to the Cloak.Cipher behaviour. You can configure a cipher module like so:

config :cloak, ModuleName,
  default: true,
  tag: "TAG", 
  # any other attributes required by the cipher

You can also have multiple ciphers configured at the same time, provided that they are not both set to default: true.

config :cloak, CipherOne,
  default: true,
  tag: "one", 
  # ...

config :cloak, CipherTwo,
  default: true,
  tag: "two", 
  # ...

Options

Both of these options are required for every cipher:

  • :default - Boolean. Determines whether this module will be the default module for encryption or decryption. The default module will be used to generate all new encrypted values.

  • :tag - Binary. Used to tag any ciphertext that the cipher module generates. This allows Cloak to decrypt a ciphertext with the correct module when you have multiple ciphers in use at the same time.

If your cipher module requires additional configuration options, you can also add those keys and values to this configuration.

# Example of custom settings for a cipher module
config :cloak, MyCustomCipher,
  default: true,
  tag: "tag",
  custom_setting1: "...",
  custom_setting2: "..."

It will be the responsibility of the cipher module to read these values from the :cloak application configuration and use them.

Provided Ciphers

If you don't see what you need here, you can use your own cipher module, provided it adheres to the Cloak.Cipher behaviour.

(And open a PR, please!)

Ecto Integration

Once Cloak is configured with a Cipher module, you can use it seamlessly with Ecto with these Ecto.Type modules:

For example, to encrypt a binary field, change your schema from this:

schema "users" do
  field :name, :binary
end

To this:

schema "users" do
  field :name, Cloak.EncryptedBinaryField
end

The name field will then be encrypted whenever it is saved to the database, using your configured cipher module. It will also be transparently decrypted whenever the user is loaded from the database.

Examples

The Cloak module can be called directly to generate ciphertext using the current default cipher module.

iex> Cloak.encrypt("Hello") != "Hello"
true

iex> Cloak.encrypt("Hello") |> Cloak.decrypt
"Hello"

iex> Cloak.version
<<"AES", 1>>

Summary

Decrypt a ciphertext with the cipher module it was encrypted with

Encrypt a value using the default cipher module

Returns the default cipher module's tag combined with the result of that cipher's version/0 function

Functions

decrypt(ciphertext)

Decrypt a ciphertext with the cipher module it was encrypted with.

encrypt/1 includes the :tag of the cipher module that generated the encryption in the ciphertext it outputs. decrypt/1 can then use this tag to find the right module on decryption.

Parameters

  • ciphertext - A binary of ciphertext generated by encrypt/1.

Example

If the cipher module responsible had the tag "AES", Cloak will find the module using that tag, strip it off, and hand the remaining ciphertext to the module for decryption.

iex> ciphertext = Cloak.encrypt("Hello world!")
...> <<"AES", _ :: bitstring>> = ciphertext
...> Cloak.decrypt(ciphertext)
"Hello world!"
encrypt(plaintext)

Encrypt a value using the default cipher module.

The :tag of the cipher will be prepended to the output. So, if the cipher was Cloak.AES.CTR, and the tag was "AES", the output would be in this format:

+-------+---------------+
| "AES" | Cipher output |
+-------+---------------+

This tagging allows Cloak to delegate decryption of a ciphertext to the correct module when you have multiple ciphers in use at the same time. (For example, this can occur while you migrate your encrypted data to a new cipher.)

Parameters

  • plaintext - The value to be encrypted.

Example

Cloak.encrypt("Hello, World!")
<<"AES", ...>>
version()

Returns the default cipher module's tag combined with the result of that cipher's version/0 function.

It is used by Cloak.Model to record which cipher was used to encrypt a row in a database table. This is very useful when migrating to a new cipher or new encryption key, because you'd be able to query your database to find records that need to be migrated.