Unknown cipher when encrypting data

Hello,
I am trying to encrypt data in migrations. Just using cloak gave me some pretty weird errors (like :crypto.strong_rand_bytes seemed to return nil for it :103:) so I am trying to replicate its functionality inline just for one migration.

Long story short, this is the problem (I am on Erlang 23.3.4.4 and Elixir 1.12.2-otp-23):

iex(1)> :crypto.crypto_one_time_aead(:aes_256_gcm, "9hs4ndXeD4p/OkcogRyVBa4pUznauN6ZRnh4eLZgsIg=", <<95, 198, 233, 21, 58, 174, 237, 201, 33, 252, 35, 26, 168, 138, 241, 61>>, "123123123123\ndfvdfv\ndfvdfvdfv", "AES256GCM", 16, true)
** (ErlangError) Erlang error: {:badarg, {'aead.c', 90}, 'Unknown cipher'}
    (crypto 4.9.0.2) :crypto.aead_cipher(:aes_256_gcm, "9hs4ndXeD4p/OkcogRyVBa4pUznauN6ZRnh4eLZgsIg=", <<95, 198, 233, 21, 58, 174, 237, 201, 33, 252, 35, 26, 168, 138, 241, 61>>, "123123123123\ndfvdfv\ndfvdfvdfv", "AES256GCM", 16, true)

One random idea that popped in my mind was to limit the IV to 12 bytes and I did but the error stays the same.

I also do this just to check:

iex(2)> :crypto.cipher_info :aes_256_gcm
%{block_size: 1, iv_length: 12, key_length: 32, mode: :gcm_mode, type: 901}

And this:

iex(3)> :crypto.supports(:ciphers)
[:chacha20, :blowfish_ecb, :blowfish_ofb64, :blowfish_cfb64, :blowfish_cbc,
 :des_ecb, :rc2_cbc, :aes_192_cbc, :aes_256_cfb128, :aes_192_cfb128,
 :aes_256_cfb8, :aes_192_cfb8, :aes_256_ecb, :aes_192_ecb, :aes_256_cbc,
 :des_ede3_cfb, :aes_128_ecb, :aes_128_cfb128, :aes_128_cfb8, :aes_128_cbc,
 :aes_256_ccm, :aes_192_gcm, :aes_192_ccm, :aes_128_ccm, :des_cfb, :des_cbc,
 :rc4, :aes_ige256, :aes_256_ctr, :aes_192_ctr, :aes_128_ctr,
 :chacha20_poly1305, :aes_256_gcm, :aes_128_gcm, :des_ede3_cbc]

I am completely lost. Can anyone help? (@voltone hope you don’t mind me tagging you)

1 Like

Maybe you hit this:

The actual supported algorithms and features depends on their availability in the actual libcrypto used. See the crypto (App) about dependencies.

[Erlang -- crypto]

Hit what exactly? The cipher seems to be supported (judging by the calls to :crypto.cipher_info and :crypto.ciphers). Are there any additional considerations?

Just a wild guess.

I get the same error {:badarg, {'aead.c', 90}, 'Unknown cipher'}, but

iex(3)> :aes_256_gcm in :crypto.supports(:ciphers)
true

It seems like get_cipher_type in aead.c#89 runs in the struct initialized here.

Not sure what you mean – maybe that my key isn’t 32 bytes long?

EDIT: Just checked, it’s 32 bytes after Base64 decoding.

Yes either that or somehow code was compiled with HAVE_GCM not set and :crypto.supports is lying.

Somehow

get_cipher_type(type, key.size)

returnsNULL

I think its your key.
When following the example in the user manual:

iex(1)> key = <<1::128>>
iex(2)> iv = <<0::128>>
iex(3)> txt = ["First bytes", "Second bytes"] 
iex(4)> aad = "some bytes"
iex(5)> :crypto.crypto_one_time_aead(:aes_128_gcm, key, iv, txt, aad, true)
{<<240, 130, 38, 96, 130, 241, 189, 52, 3, 190, 179, 213, 132, 1, 72, 192, 103,
   176, 90, 104, 15, 71, 158>>,
 <<35, 33, 39, 156, 15, 240, 252, 67, 39, 162, 221, 233, 146, 86, 21, 40>>}
iex(6)> :crypto.crypto_one_time_aead(:aes_256_gcm, key, iv, txt, aad, true)
** (ErlangError) Erlang error: {:badarg, {'aead.c', 90}, 'Unknown cipher'}

I get the “unknown cypher error” but its just the wrong key size (128 bit from example).

But:

iex(6)> key = <<1::256>>
<<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 1>>
iex(7)> iv = <<0::256>>
<<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0>>
iex(8)> :crypto.crypto_one_time_aead(:aes_256_gcm, key, iv, txt, aad, true)
{<<158, 135, 28, 93, 111, 17, 234, 107, 10, 237, 242, 151, 2, 166, 180, 14, 105,
   125, 228, 174, 101, 81, 28>>,
 <<171, 236, 22, 207, 207, 21, 255, 85, 217, 17, 153, 40, 231, 68, 190, 189>>}

your key:

iex(10)> bit_size("9hs4ndXeD4p/OkcogRyVBa4pUznauN6ZRnh4eLZgsIg=") 
352

But you should first Base.decode64 this string. The key size is fine. I have no clue what’s the problem still. :expressionless:

Why do you think that :cypto will decode the key?

This: Cloak.Vault — cloak v1.1.1

Check how the key is initialized.

Actually you know what, you could be correct. Checking…

Yep. The key is stored base64-encoded in our config and my inlining code didn’t decode it. :man_facepalming:

Just base64-decoding the key before use solved it! Thanks for being my rubber duck! :heart:

1 Like

you lost me there, you are calling crypto_one_time_aead with a String as key. No idea what that lib has to do with it. crypto_one_time_aead will just call iolist_to_binary on it and then try to find the cypher looking at the name you provide and the key-length.

1 Like

:duck:

that was definitely more fun than what I’m actually doing right now.

1 Like

I could tell! :003:

Then when I finally get the time to get back on my Elixir + Rust sqlite3 library I’ll definitely ping you. You’ll be in a world of… fun. Yes, fun, nothing else.

1 Like

Do that, looking into SQLite (and CubDB) right now to securely save configs in an embedded (nerves) device.

Dude, it’s a tragedy but it’s also for another thread / chat. I have so many good ideas and I’ll make the thing ironclad. I just don’t have the extra energy, these pre-diabetes symptoms are keeping me barely above the surface sadly. I barely manage to work and function and have some semblance of an okay diet. But things are gradually improving which is motivating.

I definitely want to get back to that and show everyone how it’s done. I am inching my way to that.

that sucks, wish you all the best, keep on improving.

Ironclad is what I’m looking for! :robot:

Just in case, if any one is looking for a complete (encryption and decryption) working example. with new API

The new (OTP> 23) API is used.
Please see the below link for detailed documenation.
Erlang -- crypto

defmodule Aes do
  @aad "AES256GCM"
  require Logger

    def encrypt(text_to_encrypt \\ "5123456789012346") do
      Logger.info("Entering encrypt function")
      Logger.info("Encryption -  Text to Encrypt #{text_to_encrypt}")
      dek = :crypto.strong_rand_bytes(32)
      iv = :crypto.strong_rand_bytes(32)

      case :crypto.crypto_one_time_aead(:aes_256_gcm, dek, iv, text_to_encrypt, @aad, true) do
        {ciphertext, ciphertag} ->
          Base.encode64(iv <> ciphertag <> ciphertext)
          Logger.info("Encryption - DEK (Data Encryption Key) in Base64 #{Base.encode64(dek)}")
          Logger.info("Encryption - IV (Intialization Vector) in Base64 #{Base.encode64(iv)}")
          Logger.info("Encryption - Encrypted text value in Base64 #{Base.encode64(iv <> ciphertag <> ciphertext)}")
          decrypt(iv <> ciphertag <> ciphertext, dek)
        _ ->
         Logger.info("Error in crypto.crypto_one_time_aead")
        end
    end


  def decrypt(encrypted_text, dek) do
    Logger.info("Entering decypt function")
    <<iv::binary-32, ciphertag::binary-16, ciphertext::binary>> = encrypted_text
    Logger.info("Decryption - DEK (Data Encryption Key) in Base64 #{Base.encode64(dek)}")
    Logger.info("Decryption - IV (Intialization Vector) in Base64 #{Base.encode64(iv)}")
    Logger.info("Decryption - CipherTag in Base64 #{Base.encode64(ciphertag)}")
    Logger.info("Decryption - CipherText in Base64 #{Base.encode64(ciphertext)}")
    Logger.info("Decryption - Decrypted text #{:crypto.crypto_one_time_aead(:aes_256_gcm, dek,iv, ciphertext, @aad, ciphertag, false)}")
    :crypto.crypto_one_time_aead(:aes_256_gcm, dek,iv, ciphertext, @aad, ciphertag, false)
  end

end

If you would like to learn how this all works together, please visit the below excellent url. (This uses the old API, be careful). It gives detailed understanding of whats under the hood).

1 Like