Code Monkey home page Code Monkey logo

cloak_ecto's Introduction

Cloak.Ecto

Hex Version Hex Docs Total Download Last Updated Build Status Coverage Status

Easily encrypt fields in your Ecto schemas. Relies on Cloak for encryption.

Usage

Cloak.Ecto helps you create Ecto.Type modules which automatically encrypt and decrypt your data. You simply define a type and set the type of your fields, and Cloak.Ecto handles the rest.

defmodule MyApp.Encrypted.Binary do
  use Cloak.Ecto.Binary, vault: MyApp.Vault
end
defmodule MyApp.EctoSchema do
  use Ecto.Schema

  schema "table_name" do
    field :encrypted_field, MyApp.Encrypted.Binary

    # ...
  end
end

When Ecto writes the fields to the database, Cloak encrypts the values into a binary blob, using a configured encryption algorithm chosen by you.

iex> Repo.insert!(%MyApp.EctoSchema{encrypted_field: "plaintext"})
08:46:08.862 [debug] QUERY OK db=3.4ms
INSERT INTO "table_name" ("encrypted_field") 
VALUES ($1) RETURNING "id", "encrypted_field" [
  <<1,10, 65, 69, 83, 46, 67, 84, 82, 46, 86, 49, 
    69, 92, 173, 219, 203, 238, 26, 58, 236, 5, 
    104, 23, 12, 10, 182, 31, 221, 89, 22, 58, 
    34, 79, 109, 30, 70, 254, 56, 93, 102, 84>>
]

Likewise, when Ecto reads the encrypted blob out of the database, Cloak will automatically decrypt the value into the intended data type at runtime.

iex> Repo.get(MyApp.EctoSchema, 1)
%MyApp.EctoSchema{encrypted_field: "plaintext"}

For complete usage instructions, see the Hex documentation.

Troubleshooting

See our troubleshooting guide for solutions to common issues.

Notable Features

  • Transparent, easy to use encryption for database fields
  • Fully compatible with umbrella projects
  • Bring your own encryption algorithm, if you want
  • Mix task for key rotation: mix cloak.migrate

Security Notes

  • Supported Algorithms: Cloak's built-in encryption modules rely on Erlang's :crypto module. Cloak supports the following algorithms out of the box:

    • AES.GCM
    • AES.CTR
  • Encrypted Data Not Searchable: Cloak uses random IVs for each ciphertext. This means that the same value will not encrypt to the same value twice. As a result, encrypted columns are not queryable. However, Cloak does provide easy ways to create hashed, searchable columns.

  • Runtime Data is not Encrypted: Cloak encrypts data at rest in the database. The data in your Ecto structs at runtime is not encrypted.

  • No Support for User-specific Encryption Keys: Cloak's Ecto.Type modules do not support user-specific encryption keys, due to limitations on the Ecto.Type behaviour. However, you can still use Cloak's ciphers to implement these in your application logic.

Use Without Ecto

If you want to use Cloak without Ecto, see cloak instead.

Local Development

To develop this library locally, you will need to install the correct version of Elixir and Postgres. The easiest way to set everything up is with Docker and docker-compose:

$ cd cloak_ecto
# Runs the bin/test script in the context of Docker
$ docker-compose run code bin/test
# To access a terminal with mix, use this command:
$ docker-compose run code bash
# Run any command of your choosing:
root@234098234oij:/app# mix docs

cloak_ecto's People

Contributors

apelsinka223 avatar barberj avatar danielberkompas avatar voltone avatar wojtekmach avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

cloak_ecto's Issues

Remove insecure example from docs

The current docs include a usage example which suggests hashing the email field with SHA256 along with a fully encrypted (secure symmetric cipher) version of the field.

By including the SHA256 version of the data, the encryption is rendered virtually useless as the same data is now also stored in the data-base on a much weaker scheme. Storing sensitive data in this way is not secure due to the below reasons and should be removed from the docs:

  • SHA256 is a public algorithm (i.e. requires no key) so anyone can compute the hash of any email address. This would make brute force attacks to effectively decrypt email addresses encrypted in this way very simple
  • Deteministic encryption (where the same output ciphertext is generated for a give plaintext and key every time) is vulnerable to inference and chosen-plaintext attacks. Put simply, if an attacker learns the result of SHA256([email protected]) then they can use that knowledge to find any other records in the database that correlate with that email address

These weaknesses may be acceptable for a given application but I think the reader should be warned if you decide to include that example in the docs. And at the very least, SHA256 should be replaced by HMAC with a key known only to the application owner.

(ArgumentError) invalid or unknown

I've set the Ecto.Type to Encrypted.Binary for my context, but I get an error.

I've verified Cloak setup and installation by Vault.encrypt("plaintext") before setting up the struct.

Here is the error:

** (ArgumentError) invalid or unknown type MaitreD.Encrypted.Binary for field :password
    lib/ecto/schema.ex:2025: Ecto.Schema.check_field_type!/3
    lib/ecto/schema.ex:1732: Ecto.Schema.__field__/4
    lib/maitre_d/grubhub_cred.ex:7: (module)
    (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6
    (elixir) lib/kernel/parallel_compiler.ex:229: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7

Is there a step missing from the documentation to expose the EctoTypes?

order by

Hi all,

I presume I can't order by in Pg as the data is encrypted and Pg doesn't know what it is?

Thanks.

SQLite3 support?

Hi! This is an awesome library, I've integrated it into apps several times before without issues.

I'm in the middle of writing a blog post for how to integrate cloak and cloak_ecto into the Phoenix auth generators, to encrypt the user's email while data is at rest. In my previous attempts, I've always used PostgreSQL as my database. But recently, I've been using SQLite3 more since it is small, lightweight and easy to get set up.

However, I'm wondering if the library does not support the SQLite database adapter for some reason, or if I am not doing something obvious here - I'm getting the following error when trying to insert fields.

[error] GenServer #PID<0.610.0> terminating
** (Ecto.ChangeError) value `"[email protected]"` for `SecureApp.Accounts.User.email` in `insert` does not match type SecureApp.Encrypted.Binary
    (ecto 3.9.4) lib/ecto/repo/schema.ex:1010: Ecto.Repo.Schema.dump_field!/6
    (ecto 3.9.4) lib/ecto/repo/schema.ex:1019: anonymous fn/6 in Ecto.Repo.Schema.dump_fields!/5
    (stdlib 4.1.1) maps.erl:411: :maps.fold_1/3
    (ecto 3.9.4) lib/ecto/repo/schema.ex:1017: Ecto.Repo.Schema.dump_fields!/5
    (ecto 3.9.4) lib/ecto/repo/schema.ex:951: Ecto.Repo.Schema.dump_changes!/7
    (ecto 3.9.4) lib/ecto/repo/schema.ex:359: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
    (secure_app 0.1.0) lib/secure_app_web/live/user_registration_live.ex:54: SecureAppWeb.UserRegistrationLive.handle_event/3

I've set up and started the Vault module, as well as integrated the Binary and HMAC types:

defmodule SecureApp.Vault do
  use Cloak.Vault, otp_app: :secure_app
end

defmodule SecureApp.Encrypted.Binary do
  use Cloak.Ecto.Binary, vault: SecureApp.Vault
end

defmodule SecureApp.Hashed.HMAC do
  use Cloak.Ecto.HMAC, otp_app: :secure_app
end

The schema is as following:

  schema "users" do
    field :email, SecureApp.Encrypted.Binary
    field :email_hashed, SecureApp.Hashed.HMAC
    field :password, :string, virtual: true, redact: true
    field :hashed_password, :string, redact: true
    field :confirmed_at, :naive_datetime

    timestamps()
  end

I haven't gotten a chance to switch the database adapter over to Postgres yet, but that will be my next debugging step. Just wondering if I am missing something here?

The code (WIP) for the blog post is public, you can find it here. It is a very minimal working example of the error I am seeing.

Thanks!

can't define label when define MyApp.Encrypted.Binary

Hello.

I'm trying to implement the same thing that we have during config MyApp.Vault.

For example:
I can have more than one cipher:
Keyword.put(config, :ciphers, [ default: {Cloak.Ciphers.AES.GCM, tag: "AES.GCM.V1", key: decode_env!("MY_KEY")}, two_factor_auth: {Cloak.Ciphers.AES.GCM, tag: "AES.GCM.V1", key: decode_env!("MY_SEC_KEY")} ])

If I use MyApp.Vault.encrypt("test", :two_factor_auth) I can choose which ciphers I want to use.
Unfortunately, I couldn't do the same thing during config a field on my Ecto Schema.

field(:two_factor_token, MyApp.Encrypted.Binary, label: :two_factor_auth)

There is this possibility? To choose which cipher my field should use?

Support custom Ecto embeds in `Cloak.Migrator`

Hello all!

I'm sorry if the question was already araised (I didn't find anything), but was is the reason why Cloak migrator doesn't support custom Ecto embeds?

https://github.com/danielberkompas/cloak_ecto/blob/master/lib/cloak_ecto/migrator.ex

defp cloak_field?({_field, {:embed, %Ecto.Embedded{}}}) do
  false
end

defp cloak_field?({_field, {:parameterized, Ecto.Embedded, %Ecto.Embedded{}}}) do
   false
end

I'm asking since I'm going to have something like this:

defmodule Vault do
  use Cloak.Vault,
    otp_app: :app
end

defmodule Vault.Value do
  use Cloak.Ecto.Binary,
    vault: Vault

  def embed_as(_format), do: :dump

  def dump(nil), do: {:ok, nil}

  def dump(value) do
    with {:ok, encrypted} <- super(value) do
      {:ok, Base.encode64(encrypted)}
    end
  end

  def load(nil), do: {:ok, nil}

  def load(value), do: super(Base.decode64!(value))
end

defmodule Partner do
  schema "partners" do
    field(:code, :string)
    embeds_one(:credentials, PartnerCredentials, on_replace: :update)
end

defmodule PartnerCredentials do
  @primary_key false
  embedded_schema do
    field(:client_id, :string)
    field(:client_secret, Vault.Value)
    field(:refresh_token, Vault.Value)
  end
end

So my client_secret and refresh_token get serialized/deserialized without problems, but I can't apply migration in case of rotating keys - credentials field will be just skipped.

Were there any problems with embeds so you don't have support of them?

Any response from you would be appreciated. Thanks!

Hashed, searchable columns

I cannot find any documentation on how to make make hashed, searchable columns to go along with my encrypted column. Any help would be appreciated, thanks for the sweet lib!

Trouble loading the Cloak.Ecto.PBKDF2 library

I am getting this error message at compile time:

module Cloak.Ecto.PBKDF2 is not loaded and could not be found

Using Erlang/OTP 24 [erts-12.2] and {:pbkdf2, "~> 2.0", github: "miniclip/erlang-pbkdf2"} in mix.exs.

How to solve this issue?

`Cloak.Ecto.SHA256.hash_string/1` break when the value is `nil`

First of all thank you for the recent release!

I've noticed some of my tests are failing due to:

@doc false
@impl Ecto.Type
def equal?(value1, value2) do
hash_string(value1) == hash_string(value2)
end
defp hash_string(string) do
if String.valid?(string), do: hash(string), else: string
end

I have a schema similar to

  schema "tokens" do
    field(:token_hash, Cloak.Ecto.SHA256)
   ...
   end

I can create a new changeset from this schema, however the initial value will be nil for :token_hash.
If I then call the following: put_change(changeset, :token_hash, "my_token") the String.valid?(nil) in hash_string(string) will fail.

I'm not entirely sure if a check needs to be added in the hash_string function to make sure first the value are not nil, or if I need to make sure first the changeset contains a valid string for the hash first.

One way I can resolve this is to add a default empty string value to field(:token_hash, Cloak.Ecto.SHA256, default: "").
Let me know if this is a non issue and feel free to close this.
Thanks

The version `1.2.0` generates an unexpected error

Recently testing the version 1.2.0, before update the library. I received the error bellow, when I try to persist a new row or read something from Postgres already encrypt.

Elixir version 1.11.3 + OTP 23

** (ErlangError) Erlang error: {:badarg, {'aead.c', 90}, 'Unknown cipher'}
(crypto 5.0) :crypto.aead_cipher_nif(:aes_256_gcm, <<...>>, <<...>>, <<...>>, "AES256GCM", <<...>>, false)

The tests pass but when I run the application the error happens.

Migrator breaks with Ecto.Enum field in the same schema

I have a schema with one encrypted field and want to rotate the keys, but the schema also has a field :state, Ecto.Enum, values: [:enabled, :disabled, :archived], default: :enabled field and it is raising an exception:

** (FunctionClauseError) no function clause matching in Code.ensure_loaded?/1

    The following arguments were given to Code.ensure_loaded?/1:

        # 1
        {:parameterized, Ecto.Enum, %{embed_as: :self, mappings: [enabled: "enabled", disabled: "disabled", archived: "archived"], on_cast: %{"archived" => :archived, "disabled" => :disabled, "enabled" => :enabled}, on_dump: %{archived: "archived", disabled: "disabled", enabled: "enabled"}, on_load: %{"archived" => :archived, "disabled" => :disabled, "enabled" => :enabled}, type: :string}}

    Attempted function clauses (showing 1 out of 1):

        def ensure_loaded?(module) when is_atom(module)

    (elixir 1.13.4) lib/code.ex:1472: Code.ensure_loaded?/1
    (cloak_ecto 1.2.0) lib/cloak_ecto/migrator.ex:77: Cloak.Ecto.Migrator.cloak_field?/1
    (elixir 1.13.4) lib/enum.ex:4034: Enum.filter_list/2
    (cloak_ecto 1.2.0) lib/cloak_ecto/migrator.ex:62: Cloak.Ecto.Migrator.cloak_fields/1

I assume a fix would have to be pushed so I'll try looking into it, but if anyone has any ideas in the meantime, it'd be appreciated.

Undocumented support for migrations (workaround)

Problem

It's common to need to do migrations in the database and convert data between encrypted fields with the following conditions:

  1. elixir is not installed (iex/mix not available)
  2. migration is fully automated with rollback support
  3. migration shouldn't start the whole runtime

Unfortunately this process is not documented and trying it myself resulted in the same error as danielberkompas/cloak#125 .

TLDR encrypt_existing_data.md solution is suboptimal.

Migration steps

I want to change a DateTime field to Date.

  1. load records
  2. decrypt field
  3. use DateTime.to_date()
  4. encrypt
  5. update

Workaround

I did manage to read through a bunch of cloak's code and come up with a solution.

  1. use MyApp.Vault.start_link() to start the Vault in Release (solves danielberkompas/cloak#125).
  2. use load() to decrypt your data.
  3. use dump() to encrypt data.

Here's my full(ish) code, because I hate it when I find a solution online and a crucial piece is left out.

defmodule MyApp.Repo.Migrations.AlterTransactionDatetime do
  use Ecto.Migration
  import Ecto.Query, warn: false
  alias MyApp.Repo
  alias MyApp.Encryption.Types

  def up do
    dates =
      from(t in "table", select: {t.id, t.datetime})
      |> Repo.all()
      |> Enum.map(fn {id, datetime_encrypted} ->
        {:ok, datetime} = Types.DateTime.load(datetime_encrypted)
        {:ok, new_val} = datetime |> DateTime.to_date() |> Types.Date.dump()
        {id, new_val}
      end)

    alter table(:table) do
      remove :datetime
      add :date, :binary
    end

    flush()

    for {id, date} <- dates do
      from(t in "table", where: t.id == ^id, update: [set: [date: ^date]])
      |> Repo.update_all([])
    end
  end
end

defmodule MyApp.Release do
  @moduledoc """
  Used for executing DB release tasks when run in production without Mix
  installed.
  """
  @app :my_app

  def migrate do
    MyApp.Vault.start_link()
    load_app()

    for repo <- repos() do
      {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
    end
  end

  [...]
end

Suggestions

  1. There should be an actual migration guide, like the above (or better since i don't know if this is how the package is supposed to be used). and encrypt_existing_data.md imho should be deprecated in it's current form.
  2. Document crucial functions like start_link, load and dump. Nothing fancy, for starters that they exist at all.
  3. Ideally the package would check if Vault is running (or if the :ets key exists) so danielberkompas/cloak#125 would have a nicer error.

Documentation: cloak.migrate.ecto mix task ignores labels and is order-dependent

Hi there. First off, thanks so much for the amazing library! It's been extremely useful for our business and maintaining security compliance.

I was looking at the documentation for the mix cloak.migrate.ecto task: https://hexdocs.pm/cloak_ecto/rotate_keys.html#content

In the example, the docs say to simply change the labels for your keys and add a new :default entry:

Then change the :default label to the new key, and demote the existing key to the :retired label.

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

This example works as expected. However, the documentation is a bit misleading. It seems that cloak ignores labels like :default and :retired. Instead, it relies on the order of the cipher entries.

For example, consider this configuration:

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

The only difference is the order of the entries. When we run mix cloak.migrate.ecto, the keys aren't rotated and remain V1. It seems the migration picks the first cipher entry.

Happy to provide more information if necessary. Thanks!

:error when decrypting field

I have a schema that was created with a migration for ecto_ch:

def change do
    create table(:settings, primary_key: false, engine: "MergeTree") do
      # The primary key for this is the organization id that the settings belong to.
      add(:organization_id, :uuid, primary_key: true)
      add(:token, :binary)

      timestamps(type: :utc_datetime)
    end
  end

with a model like so:

defmodule MyApp.Orgs.Settings do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key false
  schema "settings" do
    field(:token, MyApp.Orgs.SecretKey)

    timestamps(type: :utc_datetime)

    belongs_to(:organization, MyApp.Orgs.Organization,
      references: :id,
      type: Ecto.UUID
    )
  end

  @doc false
  def changeset(settings, attrs) do
    settings
    |> cast(attrs, [:token, :organization_id])
    |> validate_required([:token, :organization_id])
  end
end

where SecretKey is:

defmodule MyApp.Orgs.SecretKey do
  use Cloak.Ecto.Binary, vault: MyApp.Orgs.Vault
end
defmodule MyApp.Orgs.Vault do
  use Cloak.Vault, otp_app: :myapp
end


I have a simple test that makes a settings object and then lists the object. but I'm getting this error:

Assertion with == failed
     code:  assert Orgs.list_settings() == [settings]
     left:  [
              %myapp.Orgs.Settings{
                __meta__: #Ecto.Schema.Metadata<:loaded, "settings">,
                inserted_at: ~U[2024-06-12 20:10:48Z],
                token: :error,
                organization: %myapp.Orgs.Organization{
                  __meta__: #Ecto.Schema.Metadata<:loaded, "organizations">,
                  id: "9a31980f-0342-49af-8377-8e8ed9fc2852",
                  inserted_at: ~U[2024-06-12 20:10:48Z],
                  name: "some name",
                  settings: #Ecto.Association.NotLoaded<association :settings is not loaded>,
                  updated_at: ~U[2024-06-12 20:10:48Z],
                  users: #Ecto.Association.NotLoaded<association :users is not loaded>
                },
                organization_id: "9a31980f-0342-49af-8377-8e8ed9fc2852",
                updated_at: ~U[2024-06-12 20:10:48Z]
              }
            ]
     right: [
              %myapp.Orgs.Settings{
                __meta__: #Ecto.Schema.Metadata<:loaded, "settings">,
                inserted_at: ~U[2024-06-12 20:10:48Z],
                token: "some token",
                organization: %myapp.Orgs.Organization{
                  __meta__: #Ecto.Schema.Metadata<:loaded, "organizations">,
                  id: "9a31980f-0342-49af-8377-8e8ed9fc2852",
                  inserted_at: ~U[2024-06-12 20:10:48Z],
                  name: "some name",
                  settings: #Ecto.Association.NotLoaded<association :settings is not loaded>,
                  updated_at: ~U[2024-06-12 20:10:48Z],
                  users: #Ecto.Association.NotLoaded<association :users is not loaded>
                },
                organization_id: "9a31980f-0342-49af-8377-8e8ed9fc2852",
                updated_at: ~U[2024-06-12 20:10:48Z]
              }
            ]
     stacktrace:
       test/myapp/orgs_test.exs:15: (test)

Which implies that when I go to decrypt from my database, it's unable to use the vault to decrypt my encrypted key. I updated my test_helper.exs to ensure the vault was started, but no dice. MyApp.Orgs.Vault.start_link()

Missing function `:crypto.block_encrypt/4 ` in Erlang 24

Just attempted a bump to elixir 1.12 + Erlang 24 and getting a bunch of these:

     apps/web/test/web/my_test.exs:22
     ** (UndefinedFunctionError) function :crypto.block_encrypt/4 is undefined or private, use crypto:crypto_one_time/5, crypto:crypto_one_time_aead/6,7 or crypto:crypto_(dyn_iv)?_init + crypto:crypto_(dyn_iv)?_update + crypto:crypto_final instead
     stacktrace:
       (crypto 5.0.1) :crypto.block_encrypt(:aes_gcm, <<195, 225, 165, 141, 41, 78, 62, 205, 98, 180, 191, 110, 234, 172, 72, 184, 62, 87, 136, 164, 190, 51, 242, 243, 163, 183, 119, 33, 180, 102, 137, 119>>, <<120, 219, 37, 247, 92, 246, 21, 28, 46, 15, 17, 201, 227, 63, 247, 245>>, {"AES256GCM", "secret"})
       (cloak 1.0.3) lib/cloak/ciphers/aes_gcm.ex:46: Cloak.Ciphers.AES.GCM.encrypt/2
       (web 0.1.0) lib/cloak_ecto/type.ex:37: Web.Encrypted.Binary.dump/1
       (ecto 3.6.1) lib/ecto/type.ex:914: Ecto.Type.process_dumpers/3
       (ecto 3.6.1) lib/ecto/repo/schema.ex:953: Ecto.Repo.Schema.dump_field!/6
       (ecto 3.6.1) lib/ecto/repo/schema.ex:966: anonymous fn/6 in Ecto.Repo.Schema.dump_fields!/5
       (stdlib 3.15) maps.erl:410: :maps.fold_1/3
       (ecto 3.6.1) lib/ecto/repo/schema.ex:964: Ecto.Repo.Schema.dump_fields!/5
       (ecto 3.6.1) lib/ecto/repo/schema.ex:897: Ecto.Repo.Schema.dump_changes!/6
       (ecto 3.6.1) lib/ecto/repo/schema.ex:335: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
       (ecto 3.6.1) lib/ecto/repo/schema.ex:942: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/6
       (ecto_sql 3.6.1) lib/ecto/adapters/sql.ex:1005: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
       (db_connection 2.4.0) lib/db_connection.ex:1512: DBConnection.run_transaction/4

Thanks for the great project!

Create unique contraint for encrypted or hashed column

I want to create a unique constraint on an encrypted column, but since it's :binary (which gets stored as bytea in Postgres), the unique index doesn't work as expected. I followed the _hash instructions, but that value is stored as :binary too, so has the same limitation. Am I'm missing something or is this functionality not supported? Is there a way to store the value as a base64 string instead of binary?

Add a `Decimal` type

i use Decimal for sensitive numbers and cloak_ecto doesn't seem to support it.

it doesn't look too hard to implement, but can someone check this? seems to be working for me.

defmodule Cloak.Ecto.Decimal do
  defmacro __using__(opts) do
    opts = Keyword.merge(opts, vault: Keyword.fetch!(opts, :vault))

    quote do
      alias Decimal, as: D
      use Cloak.Ecto.Type, unquote(opts)

      def cast(value), do: Ecto.Type.cast(:decimal, value)

      def before_encrypt(value) do
        case Ecto.Type.cast(:decimal, value) do
          {:ok, d} -> D.to_string(d)
          _error -> :error
        end
      end

      def after_decrypt(value) do
        case D.new(value) do
          %D{} = d -> d
          _error -> :error
        end
      end
    end
  end
end

Not possible to compile with Ecto 3.6.0

Hi,

when we wanted to upgrade Ecto to 3.6.0 (diff, changelog but after upgrade it's not possible to compile our projects.

There is error:

= Compilation error in file lib/our_app/models/model.ex ==
** (ArgumentError) argument error
    (stdlib 3.14) :ets.lookup(OurApp.Vault.Config, :config)
    (cloak 1.0.3) lib/cloak/vault.ex:285: Cloak.Vault.read_config/1
    lib/cloak/vault.ex:266: OurApp.Vault.json_library/0
    lib/cloak_ecto/types/map.ex:60: OurApp.Vault.Map.before_encrypt/1
    lib/cloak_ecto/type.ex:36: OurApp.Vault.Map.dump/1
    (ecto 3.6.0) lib/ecto/schema.ex:2091: Ecto.Schema.validate_default!/2
    (ecto 3.6.0) lib/ecto/schema.ex:1830: Ecto.Schema.__field__/4
    lib/our_app/models/model.ex:19: (module)
    (stdlib 3.14) erl_eval.erl:680: :erl_eval.do_apply/6
    (elixir 1.11.2) lib/kernel/parallel_compiler.ex:314: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7

Looks like it want to load configuration from ETS which is not available at compile time.

We figured out that default value for field is creating this issue.
This works:

field :responses, OurApp.Vault.Map

This doesn't work

field :responses, OurApp.Vault.Map, default: %{}

Config:

defmodule OurApp.Vault do
  use Cloak.Vault, otp_app: :our_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_SECRET")}
      )

    {:ok, config}
  end

  defp decode_env!(var), do: System.get_env(var) |> decode!()

  defp decode!(nil), do: Application.get_all_env(:our_app)[OurApp.Vault][:key] |> Base.decode64!()
  defp decode!(var), do: Base.decode64!(var)
end

Any suggestions to fix this?

Cloak.Ecto.Type does not conform to the Ecto.Type behavior

The Cloak.Ecto.Type module does not conform to the Ecto.Type behavior. For example, equal/3 is not implemented.

Because of this, following the cloak_ecto usage instructions results in a custom Ecto type which does not conform to the Ecto.Type behavior. This can result in UndefinedFunctionError instances when Ecto tries to use the custom type in a way that is not implemented:

** (UndefinedFunctionError) function Application.Encrypted.Binary.equal?/2 is undefined or private

I believe either Cloak.Ecto.Type should be updated to use Ecto.Type such that the default implementations can be used as fallbacks, or the usage instructions should be updated to let consumers of use Cloak.Ecto.Binary know that they either need to use Ecto.Type or define the missing methods.

In multi-tenant setting

Is there any way to make cloak_ecto use a unique key for every tenant?

The runtime configuration Cloak.Ecto.HMAC.init/1 is very useful but can it be invoked per process?

Unknown type error in Dialyxir when using cloak_ecto with typed_ecto_schema

When using cloak_ecto in a typed Ecto Schema (see https://hexdocs.pm/typed_ecto_schema/), Dialyxir (see https://hex.pm/packages/dialyxir) halts with an unknown type error:

$ mix dialyzer

[…]

lib/db/contact.ex:0:unknown_type
Unknown type: Cloak.Ecto.SHA256.t/0.
________________________________________________________________________________
lib/db/contact.ex:0:unknown_type
Unknown type: DB.Encrypted.Binary.t/0.

Ecto Schema:

defmodule DB.Contact do

  typed_schema "contact" do
    field(:email, DB.Encrypted.Binary)
  end
end
defmodule DB.Encrypted.Binary do
  use Cloak.Ecto.Binary, vault: Transport.Vault
end

See https://github.com/etalab/transport-site/blob/master/apps/transport/lib/db/contact.ex, to reproduce the problem remove the line in .dialyzer_ignore.exs.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.