Skip to content

Instantly share code, notes, and snippets.

@potatosalad
Created August 11, 2018 19:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save potatosalad/c1a787819b93c5fdb8b77958f00c8723 to your computer and use it in GitHub Desktop.
Save potatosalad/c1a787819b93c5fdb8b77958f00c8723 to your computer and use it in GitHub Desktop.
defmodule ExampleEncryptor do
def encrypt(plain_text, password) do
salt = :crypto.strong_rand_bytes(32)
iterations = 4096
kek = derive_key_encryption_key(password, salt, iterations, 32, :sha512)
kiv = :crypto.strong_rand_bytes(12)
cek = :crypto.strong_rand_bytes(32)
civ = :crypto.strong_rand_bytes(12)
{cek_cipher_text, cek_cipher_tag} = :crypto.block_encrypt(:aes_gcm, kek, kiv, {<<>>, cek})
protected = %{
"alg" => "PBES2-HS512+A256GCMKW",
"p2i" => iterations,
"p2s" => salt,
"iv" => kiv,
"tag" => cek_cipher_tag,
"enc" => "A256GCM"
}
aad = :erlang.term_to_binary(protected)
{cipher_text, cipher_tag} = :crypto.block_encrypt(:aes_gcm, cek, civ, {aad, plain_text})
:erlang.term_to_binary(%{
"protected" => aad,
"encrypted_key" => cek_cipher_text,
"iv" => civ,
"ciphertext" => cipher_text,
"tag" => cipher_tag
})
end
def decrypt(encrypted, password) do
case maybe_binary_to_term(encrypted) do
{:ok,
%{"protected" => aad, "encrypted_key" => cek_cipher_text, "iv" => civ, "ciphertext" => cipher_text, "tag" => cipher_tag}} ->
case maybe_binary_to_term(aad) do
{:ok,
%{
"alg" => "PBES2-HS512+A256GCMKW",
"p2i" => iterations,
"p2s" => salt,
"iv" => kiv,
"tag" => cek_cipher_tag,
"enc" => "A256GCM"
}} ->
kek = derive_key_encryption_key(password, salt, iterations, 32, :sha512)
case :crypto.block_decrypt(:aes_gcm, kek, kiv, {<<>>, cek_cipher_text, cek_cipher_tag}) do
cek when is_binary(cek) and bit_size(cek) === 256 ->
case :crypto.block_decrypt(:aes_gcm, cek, civ, {aad, cipher_text, cipher_tag}) do
plain_text when is_binary(plain_text) ->
{:ok, plain_text}
:error ->
:error
end
_ ->
:error
end
end
_ ->
:error
end
end
def derive_key_encryption_key(password, salt, iterations, derived_key_length, hash) when hash in [:sha256, :sha384, :sha512] do
prf_output_length = byte_size(:crypto.hmac(hash, <<>>, <<>>))
:pubkey_pbe.pbdkdf2(password, salt, iterations, derived_key_length, &:crypto.hmac/4, hash, prf_output_length)
end
@doc false
defp maybe_binary_to_term(binary) do
try do
term = :erlang.binary_to_term(binary, [:safe])
{:ok, term}
catch
_, _ ->
:error
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment