How To Upgrade From 0.6.x to 0.7.x

Cloak 0.7 introduced a number of important changes.

  • Encryption is now performed through Cloak.Vault modules
  • Ciphertext no longer contains a module_tag
  • Ecto types are now local to your project
  • You no longer need an :encryption_version field

Install Cloak 0.7

Update your cloak dependency to 0.7 or later.

{:cloak, "~> 0.7.0"}

Create a Vault

Create a vault module for your project. (Or more than one, if you want!)

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

You will need to move your existing configuration to the vault. For example, if you had this configuration:

config :cloak, Cloak.AES.CTR,
  tag: "AES",
  default: true,
  keys: [
    %{tag: <<1>>, key: :base64.decode("..."), default: true}
  ]

You would convert it to the following:

config :my_app, MyApp.Vault,
  ciphers: [
    default: {Cloak.Ciphers.AES.CTR, tag: "AES.V2", key: Base.decode64("...")},
    retired: {Cloak.Ciphers.Deprecated.AES.CTR, module_tag: "AES", tag: <<1>>, key: Base.decode64("...")}
  ]

Notice that the tag: "AES" became module_tag: "AES" in the :retired cipher configuration.

Alternatively, if your keys are stored in environment variables, you could configure the vault using the Cloak.Vault.init/1 callback:

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

  @impl Cloak.Vault
  def init(config) do
    config =
      Keyword.put(config, :ciphers, [
        default: {Cloak.Ciphers.AES.CTR, tag: "AES.V2", key: decode_env("CLOAK_KEY")},
        retired: {Cloak.Ciphers.Deprecated.AES.CTR, module_tag: "AES", tag: <<1>>, key: decode_env("CLOAK_KEY")}
      ])

    {:ok, config}
  end

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

Create Project-Specific Ecto Types

For each type of encrypted field you have, define a local type. For example, if you had the following schema:

defmodule MyApp.Accounts.User do
  use Ecto.Schema

  import Ecto.Changeset

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

  @doc false
  def changeset(struct, attrs \\ %{}) do
    struct
    |> cast(attrs, [:name])
    |> put_change(:encryption_version, Cloak.version())
  end
end

You would define a project-specific field:

defmodule MyApp.Encrypted.Binary do
  use Cloak.Fields.Binary, vault: MyApp.Vault
end

And then replace Cloak.EncryptedBinaryField in your schema:

schema "users" do
  field :name, MyApp.Encrypted.Binary,
  field :encryption_version
end 

Finally, you’d remove the :encryption_version field as it is no longer needed.

# In migration...
alter table(:users) do
  remove :encryption_version
end

# In your changeset...
@doc false
def changeset(struct, attrs \\ %{}) do
  struct
  |> cast(attrs, [:name])
end

Migrate Existing Data

To convert ciphertext en masse from the old v0.6 format to the new v0.7 format, you’ll need to run mix cloak.migrate as shown in its documentation.

mix cloak.migrate -r MyApp.Repo -s MyApp.Schema

Remove :retired Cipher

Now that the data has been migrated to the new v0.7 format, you can remove the :retired cipher from your configuration.

config :my_app, MyApp.Vault,
  ciphers: [
    default: {Cloak.Ciphers.AES.CTR, tag: "AES.V2", key: Base.decode64("...")}
  ]