-
Notifications
You must be signed in to change notification settings - Fork 30
/
sha_256.ex
92 lines (70 loc) · 2.03 KB
/
sha_256.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
defmodule Cloak.Ecto.SHA256 do
@moduledoc """
An `Ecto.Type` which hashes the field value using the SHA256 algorithm.
## Why
If you store a hash of a field's value, you can then query on it as a proxy
for the encrypted field. This works because SHA256 is deterministic and
always results in the same value, while secure encryption does not. Be
warned, however, that hashing will expose which fields have the same value,
because they will contain the same hash.
## Security
For a more secure hashing method, see one of the following alternatives:
- `Cloak.Ecto.HMAC`
- `Cloak.Ecto.PBKDF2`
## Usage
Create the hash field with the type `:binary`. Add it to your schema
definition like this:
schema "table" do
field :field_name, MyApp.Encrypted.Binary
field :field_name_hash, Cloak.Ecto.SHA256
end
Ensure that the hash is updated whenever the target field changes with the
`put_change/3` function:
def changeset(struct, attrs \\\\ %{}) do
struct
|> cast(attrs, [:field_name, :field_name_hash])
|> put_change(:field_name_hash, get_field(changeset, :field_name))
end
Query the Repo using the `:field_name_hash` in any place you would typically
query by `:field_name`.
user = Repo.get_by(User, email_hash: "user@email.com")
"""
@behaviour Ecto.Type
@doc false
@impl Ecto.Type
def type, do: :binary
@doc false
@impl Ecto.Type
def cast(nil) do
{:ok, nil}
end
@impl Ecto.Type
def cast(value) do
{:ok, to_string(value)}
end
@doc false
@impl Ecto.Type
def dump(value) do
{:ok, hash(value)}
end
@doc false
@impl Ecto.Type
def load(value) do
{:ok, value}
end
@doc false
@impl Ecto.Type
def embed_as(_), do: :self
@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
@doc false
def hash(value) do
:crypto.hash(:sha256, value)
end
end