# One-off encryption?

**URL:** <https://forum.elixirforum.com/t/one-off-encryption/43282>\
**Category:** Questions\
**Tags:** encryption\
**Created:** [October 22, 2021, 7:05pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282 "2021-10-22T19:05:11Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![dimitarvp](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/32/38664_2.png) [@dimitarvp](https://forum.elixirforum.com/u/dimitarvp)\
**Post date:** [October 22, 2021, 7:05pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/1 "2021-10-22T19:05:11Z")

</div>

Hey all,  
I am looking for a way to encode and then encrypt a payload that will later be passed to a webhook in the web app.

We’re talking something like “put these two options in your `config/config.exs` and then call these two functions”.

What’s a very quick and low-friction way to encrypt a binary (and subsequently decrypt it)? I am not looking for the best security here; I am looking for something to discourage a potential attacker that might be able to sniff an HTTP request with an encoded parameter in it.

---

<div class="post-metadata">

**Author:** ![hauptbenutzer](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/hauptbenutzer/32/23011_2.png) [@hauptbenutzer](https://forum.elixirforum.com/u/hauptbenutzer)\
**Post date:** [October 22, 2021, 7:25pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/2 "2021-10-22T19:25:16Z")

</div>

Hi! I see two scenarios (sorry if I’m misreading your post):

1. You do not care about the encoded information being read but just want to make sure it was not tempered with. In this case signing might be sufficient and you can use [Phoenix.Token — Phoenix v1.8.8](https://hexdocs.pm/phoenix/Phoenix.Token.html) or similar which is low-friciton if you’re already using phoenix.
2. If you want the encoded data to be safe from prying eyes use something like [GitHub - danielberkompas/cloak: Elixir encryption library designed for Ecto · GitHub](https://github.com/danielberkompas/cloak) which implements best practices around erlang crypto so you don’t need to worry about the details (like IVs). This is very close to "put these two options in your `config/config.exs` and then call these two functions”

---

<div class="post-metadata">

**Author:** ![dimitarvp](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/32/38664_2.png) [@dimitarvp](https://forum.elixirforum.com/u/dimitarvp)\
**Post date:** [October 22, 2021, 7:37pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/3 "2021-10-22T19:37:54Z")

</div>

I was hoping for something without an external dependency. Basically a way to call `:crypto` which is a bit confusing to me.

BTW I want the data secret. I assume encrypting it well also prevents tampering.

But if I can’t do that I’ll definitely use Cloak.

---

<div class="post-metadata">

**Author:** ![hauptbenutzer](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/hauptbenutzer/32/23011_2.png) [@hauptbenutzer](https://forum.elixirforum.com/u/hauptbenutzer)\
**Post date:** [October 22, 2021, 7:45pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/4 "2021-10-22T19:45:43Z")

</div>

Well you can certainly choose to use `:crypto` directly but you’ll have to take care of IVs and padding yourself. We ended up doing something like this (note that this has a hardcoded IV size of 16):

```elixir
defmodule CryptoOneTime do
  require Logger 
  
  def encrypt_binary(data) when is_binary(data) do
    initialization_vector = :crypto.strong_rand_bytes(16)
    plaintext = pad(data, 16)
    encrypted_text = :crypto.crypto_one_time(:aes_128_cbc, secret_key(), initialization_vector, plaintext, true)

    :base64.encode(initialization_vector <> encrypted_text)
  end

  def decrypt_binary(ciphertext) when is_binary(ciphertext) do
    <<initialization_vector::binary-16, ciphertext::binary>> = Base.decode64!(ciphertext)

    plaintext =
      :aes_128_cbc
      |> :crypto.crypto_one_time(secret_key(), initialization_vector, ciphertext, false)
      |> unpad()

    {:ok, plaintext}
  rescue
    error in ArgumentError ->
      Logger.error(Exception.format(:error, error, __STACKTRACE__ ))
      :error
  end

  def decrypt_binary(_non_binary_value), do: :error

  defp secret_key do
    # this needs to be 128 bits of class-a randomness
  end

  defp unpad(data) do
    :binary.part(data, 0, byte_size(data) - :binary.last(data))
  end

  defp pad(data, block_size) do
    padding = block_size - rem(byte_size(data), block_size)
    data <> :binary.copy(<<padding>>, padding)
  end
end

```

---

<div class="post-metadata">

**Author:** ![derek-zhou](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/derek-zhou/32/19943_2.png) [@derek-zhou](https://forum.elixirforum.com/u/derek-zhou)\
**Post date:** [October 22, 2021, 7:50pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/5 "2021-10-22T19:50:36Z")

</div>

The best way to keep something secret is not to transmit it, encrypted or not. I would put said payload in a database, get the sequence id and just send the id with [hashids](https://www.hex.pm/packages/hashids)

This way you send a very short string regardless how large is the payload.

---

<div class="post-metadata">

**Author:** ![dimitarvp](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/32/38664_2.png) [@dimitarvp](https://forum.elixirforum.com/u/dimitarvp)\
**Post date:** [October 22, 2021, 8:08pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/6 "2021-10-22T20:08:18Z")

</div>

That’s what I want to do. Everything is in the DB, I just want the ID sent back securely. How secure is `hashids` though?

---

<div class="post-metadata">

**Author:** ![dimitarvp](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/32/38664_2.png) [@dimitarvp](https://forum.elixirforum.com/u/dimitarvp)\
**Post date:** [October 22, 2021, 8:08pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/7 "2021-10-22T20:08:59Z")

</div>

That looks good. I assume I’ll have to store the IV in `config/config.exs` and not only the secret key?

---

<div class="post-metadata">

**Author:** ![hauptbenutzer](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/hauptbenutzer/32/23011_2.png) [@hauptbenutzer](https://forum.elixirforum.com/u/hauptbenutzer)\
**Post date:** [October 22, 2021, 8:14pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/8 "2021-10-22T20:14:37Z")

</div>

The IV is computed for every encrypt so that encrypting the same payload twice yields different results (similar idea as a salt). It’s prepended to the encrypted payload, so that it can be read and used for decryption. So you only need to keep the secret key in your `config.exs` (or `runtime.exs` more likely 🙂)

---

<div class="post-metadata">

**Author:** ![dimitarvp](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/32/38664_2.png) [@dimitarvp](https://forum.elixirforum.com/u/dimitarvp)\
**Post date:** [October 22, 2021, 8:15pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/9 "2021-10-22T20:15:25Z")

</div>

Thank you. Let me try it and I’ll comment back!

I just keep forgetting those encryption primitives, hence my question here. 😃

---

<div class="post-metadata">

**Author:** ![derek-zhou](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/derek-zhou/32/19943_2.png) [@derek-zhou](https://forum.elixirforum.com/u/derek-zhou)\
**Post date:** [October 22, 2021, 8:17pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/10 "2021-10-22T20:17:43Z")

</div>

Hashid is not very secure. to add extra protection on tempering, you can append a sha3 hash.

---

<div class="post-metadata">

**Author:** ![dimitarvp](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/32/38664_2.png) [@dimitarvp](https://forum.elixirforum.com/u/dimitarvp)\
**Post date:** [October 22, 2021, 8:19pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/11 "2021-10-22T20:19:22Z")

</div>

I see. But this seems to become a homegrown cryptography solution, which is something I want to avoid.

---

<div class="post-metadata">

**Author:** ![derek-zhou](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/derek-zhou/32/19943_2.png) [@derek-zhou](https://forum.elixirforum.com/u/derek-zhou)\
**Post date:** [October 22, 2021, 8:29pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/12 "2021-10-22T20:29:08Z")

</div>

for inputs that lack entropy such as short integers, any encryption is not going to survive serious crpto attack. A breakable integer id and a secure hash of the original and un-transmitted payload still offer quite some protection.

EDIT: On a second thought, you can just send the secure hash, and use the hash as the primary key of your table to fetch the original payload. hashids is not needed here.

---

<div class="post-metadata">

**Author:** ![dimitarvp](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/32/38664_2.png) [@dimitarvp](https://forum.elixirforum.com/u/dimitarvp)\
**Post date:** [October 22, 2021, 8:47pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/13 "2021-10-22T20:47:31Z")

</div>

I get what you are saying. We’re not expecting serious attacks. I just don’t want that integer ID flying around naked on the net (even though it goes through HTTPS).

I still might go with `hashids` btw. I’ll take an hour or so Soon™ to evaluate both suggested approaches. Your has the advantage of being super simple while offering adequate protection.

---

<div class="post-metadata">

**Author:** ![hauptbenutzer](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/hauptbenutzer/32/23011_2.png) [@hauptbenutzer](https://forum.elixirforum.com/u/hauptbenutzer)\
**Post date:** [October 22, 2021, 8:49pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/14 "2021-10-22T20:49:13Z")

</div>

We’re no crypto experts either, so I feel obligated to reference [this SO post](https://stackoverflow.com/a/22958889/1077318) 😃 the chosen cypher and padding seemed a good fit for our use-case where we’re encrypting a user ID that goes through a 3rd-party service and comes back via webhook as well.

---

<div class="post-metadata">

**Author:** ![lud](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/lud/32/14382_2.png) [@lud](https://forum.elixirforum.com/u/lud)\
**Post date:** [October 22, 2021, 8:50pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/15 "2021-10-22T20:50:08Z")

</div>

Why not just generate a random string, store it in the database and send that ? (or a hash like @derek-zhou said.)

So even if someone sniffs it, it is meaningless. Is there a fundamental problem to send a value that is also stored in the database, it the value is just a one-time key for fetching?

---

<div class="post-metadata">

**Author:** ![Nicd](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/nicd/32/2357_2.png) [@Nicd](https://forum.elixirforum.com/u/Nicd)\
**Post date:** [October 22, 2021, 8:53pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/16 "2021-10-22T20:53:06Z")

</div>

If you already use Phoenix, you can use [Phoenix.Token.encrypt/4](https://hexdocs.pm/phoenix/Phoenix.Token.html#encrypt/4) to encrypt a token with a given secret.

---

<div class="post-metadata">

**Author:** ![dimitarvp](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/32/38664_2.png) [@dimitarvp](https://forum.elixirforum.com/u/dimitarvp)\
**Post date:** [October 22, 2021, 8:54pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/17 "2021-10-22T20:54:34Z")

</div>

> [@lud](#):
>
> Why not just generate a random string, store it in the database and send that ? (or a hash like @derek-zhou said.)

That… I haven’t thought of that. That actually might be the best solution.

Thanks for showing me that I am dumb today. I needed it. :003:

---

<div class="post-metadata">

**Author:** ![dimitarvp](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/32/38664_2.png) [@dimitarvp](https://forum.elixirforum.com/u/dimitarvp)\
**Post date:** [October 22, 2021, 8:54pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/18 "2021-10-22T20:54:53Z")

</div>

Nope, a very plain `Plug` project so Phoenix is off limits for now.

---

<div class="post-metadata">

**Author:** ![hauptbenutzer](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/hauptbenutzer/32/23011_2.png) [@hauptbenutzer](https://forum.elixirforum.com/u/hauptbenutzer)\
**Post date:** [October 22, 2021, 9:04pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/19 "2021-10-22T21:04:12Z")

</div>

One caveat: In our case it was important that the same db record (a user) was not mapped to the same hash/encrypted text, because that would allow the external system to essentially track activity. If that’s no concern in your use case, some random bytes in your database are your best bet, indeed!

---

<div class="post-metadata">

**Author:** ![dimitarvp](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/32/38664_2.png) [@dimitarvp](https://forum.elixirforum.com/u/dimitarvp)\
**Post date:** [October 22, 2021, 9:23pm UTC](https://forum.elixirforum.com/t/one-off-encryption/43282/20 "2021-10-22T21:23:43Z")

</div>

We use several systems and our app is the glue + the persistent layer keeping track of it all. Your point is valid and well understood – but happily not critical in this case.

Worst case scenario is that one of those external systems will know we’re sending some numbers back to our webhook. And those numbers expire and are unusable minutes later.
