cloak v1.0.0-alpha.0 Cloak.Vault behaviour View Source

Encrypts and decrypts data, using a configured cipher.

Create Your Vault

Define a module in your application that uses Cloak.Vault.

defmodule MyApp.Vault do
  use Cloak.Vault, otp_app: :my_app
end

Configuration

The :otp_app option should point to an OTP application that has the vault configuration.

For example, the vault:

defmodule MyApp.Vault do
  use Cloak.Vault, otp_app: :my_app
end

Could be configured with Mix configuration like so:

config :my_app, MyApp.Vault,
  json_library: Jason,
  ciphers: [
    default: {Cloak.Ciphers.AES.GCM, tag: "AES.GCM.V1", key: <<...>>}
  ]

The configuration options are:

  • :json_library: Used to convert data types like lists and maps into binary so that they can be encrypted. (Default: Jason)

  • :ciphers: a list of Cloak.Cipher modules the following format:

    {:label, {CipherModule, opts}}

    The first configured cipher in the list is the default for encrypting all new data, regardless of its label. This behaviour can be overridden on a field-by-field basis.

    The opts are specific to each cipher module. Check their codumentation for what each cipher requires.

Runtime Configuration

Because Vaults are GenServers, they can be configured at runtime using the init/1 callback. This allows you to easily fetch values like environment variables in a reliable way.

The configuration from the :otp_app is passed as the first argument to the callback, allowing you to append to or change it at will.

defmodule MyApp.Vault do
  use Cloak.Vault, otp_app: :my_app

  @impl GenServer
  def init(config) do
    config =
      Keyword.put(config, :ciphers, [
        default: {Cloak.Ciphers.AES.GCM, tag: "AES.GCM.V1", key: decode_env!("CLOAK_KEY")}
      ])

    {:ok, config}
  end

  defp decode_env!(var) do
    var
    |> System.get_env()
    |> Base.decode64!()
  end
end

You can also pass configuration to vaults via start_link/1:

MyApp.Vault.start_link(ciphers: [
  default: {Cloak.Ciphers.AES.GCM, tag: "AES.GCM.V1", key: key}
])

Supervision

Because Vaults are GenServers, you’ll need to add your vault to your supervision tree in application.ex or whichever supervisor you prefer.

children = [
  MyApp.Vault
]

If you want to pass in configuration values at runtime, you can do so:

children = [
  {MyApp.Vault, ciphers: [...]}
]

Usage

You can use the vault directly by calling its functions.

MyApp.Vault.encrypt("plaintext")
# => {:ok, <<...>>}

MyApp.Vault.decrypt(ciphertext)
# => {:ok, "plaintext"}

See the documented callbacks below for the functions you can call.

Performance Notes

Vaults are not bottlenecks. They simply store configuration in an ETS table named after the Vault, e.g. MyApp.Vault.Config. All encryption and decryption is performed in your local process, reading configuration from the vault’s ETS table.

Link to this section Summary

Callbacks

Like decrypt/1, but raises any errors

Decrypts a binary with the configured cipher that generated the binary. Automatically detects which cipher to use, based on the ciphertext

Like encrypt/1, but raises any errors

Like encrypt/2, but raises any errors

Encrypts a binary using the first configured cipher in the vault’s configured :ciphers list

Encrypts a binary using the vault’s configured cipher with the corresponding label

The JSON library the vault uses to convert maps and lists into JSON binaries before encryption

Link to this section Types

Link to this type ciphertext() View Source
ciphertext() :: binary()
Link to this type plaintext() View Source
plaintext() :: binary()

Link to this section Callbacks

Link to this callback decrypt!(ciphertext) View Source
decrypt!(ciphertext()) :: plaintext() | no_return()

Like decrypt/1, but raises any errors.

Link to this callback decrypt(ciphertext) View Source
decrypt(ciphertext()) :: {:ok, plaintext()} | {:error, Exception.t()}

Decrypts a binary with the configured cipher that generated the binary. Automatically detects which cipher to use, based on the ciphertext.

Link to this callback encrypt!(plaintext) View Source
encrypt!(plaintext()) :: ciphertext() | no_return()

Like encrypt/1, but raises any errors.

Link to this callback encrypt!(plaintext, label) View Source
encrypt!(plaintext(), label()) :: ciphertext() | no_return()

Like encrypt/2, but raises any errors.

Link to this callback encrypt(plaintext) View Source
encrypt(plaintext()) :: {:ok, ciphertext()} | {:error, Exception.t()}

Encrypts a binary using the first configured cipher in the vault’s configured :ciphers list.

Link to this callback encrypt(plaintext, label) View Source
encrypt(plaintext(), label()) :: {:ok, ciphertext()} | {:error, Exception.t()}

Encrypts a binary using the vault’s configured cipher with the corresponding label.

Link to this callback json_library() View Source
json_library() :: module()

The JSON library the vault uses to convert maps and lists into JSON binaries before encryption.