Symmetric encryption in elixir

Is there a simple library to encrypt and decrypt plain_text with a secret like

encrypted_text = encrypt!(plain_text, secret)

then

{:ok, decrypted_text} = decrypt(encrypted_text, user_input)

https://www.erlang.org/doc/man/crypto.html

Erlangs :crypto library would be the main target, :crypto.crypto_one_time probably matches your ask closest but there are restrictions on key sizes, etc, padding, etc. You can’t just throw a blob and a password at it.

iv = :crypto.strong_rand_bytes(16)
key = :crypto.hash(:sha256, "dont tell anyone")
shareable = :crypto.crypto_one_time(:chacha20, key, iv, "hunter1", encrypt: true)
decoded = :crypto.crypto_one_time(:chacha20, key, iv, shareable, encrypt: false)

I would look to some guides before doing anything more than toys.

1 Like

Please, do not use such method for anything that has any value.

2 Likes

Offering users a way to save secret notes is that of value?

You might try this…

1 Like

The thing is that example given by @soup is super malleable:

iv = :crypto.strong_rand_bytes(16)
key = :crypto.hash(:sha256, "dont tell anyone")
shareable = :crypto.crypto_one_time(:chacha20, key, iv, "hunter1", encrypt: true)
modified = :crypto.exor(shareable, <<0::size(6)-unit(8), 3>>)
decoded = :crypto.crypto_one_time(:chacha20, key, iv, modified, encrypt: false)

decoded == "hunter2"

What you want there is AEAD:

iv = :crypto.strong_rand_bytes(12)
key = :crypto.hash(:sha256, "dont tell anyone")
{shareable, tag} = :crypto.crypto_one_time_aead(:chacha20_poly1305, key, iv, "hunter1", "", encrypt: true)
decoded = :crypto.crypto_one_time_aead(:chacha20_poly1305, key, iv, shareable, tag, encrypt: false)

PS: I will even ignore there a fact that ChaCha20 should not be used with random IVs, as it is not secure as the IV size is too small. XChaCha20 solves that problem, but it is not available in OpenSSL right now (but it can easily be implemented on top of :crypto). So the proper way would be:

# `packet_no` should be incremented on each new packet
iv = <<packet_no::size(12)-unit(8)>>
key = :crypto.hash(:sha256, "dont tell anyone")
{shareable, tag} = :crypto.crypto_one_time_aead(:chacha20_poly1305, key, iv, "hunter1", "", encrypt: true)
decoded = :crypto.crypto_one_time_aead(:chacha20_poly1305, key, iv, shareable, tag, encrypt: false)

PS2: Obviously you also shouldn’t use SHA-256 for key derivation from the secret. That is another thing. So as you can see, there is HELL LOT of pitfalls that you can encounter. People often say “do not write your own crypto”, but they forgot that it is not about primitives, because good primitives (like ChaCha20) are easy to write on your own and it is hard to do something wrong. What they really mean is that you should not write your own cryptographic protocols as you will fail fast and hard.

6 Likes

fail fast is good. In the sense that you immediately recognize sth is wrong. But with flawed crypto you think you are good, until you are not.

There I meant that you fail immediately as soon as you start working, not that the compiler or anything will fail fast to inform you that something is wrong.