Help with reading 'typespecs' for Erlang `crypto` module

I’m working on upgrading Elixir and Erlang for a potential issue and I need to rewrite some code that uses the crypto module.

I thought I should be able to call the new functions like this:

    :crypto.crypto_one_time(:aes_ctr, key, iv, data, encrypt: false)

But I’m seeing an error like this when I call the function with the above code:

** (ErlangError) Erlang error: {:badarg, {'api_ng.c', 141}, 'Unknown cipher'}
    (crypto 4.8.3) :crypto.ng_crypto_one_time_nif(:aes_ctr, <<9, 130, 176, 54, 178, 62, 33, 59, 142, 45, 42, 47, 12, 217, 143, 221, 41, 191, 194, 21, 245, 247, 80, 88, 206, 73, 133, 90, 164, 25, 54, 10>>, <<58, 134, 203, 139, 138, 182, 47, 173, 47, 192, 203, 97, 113, 39, 21, 2>>, "This is a test message", true, :undefined)

The docs for the crypto module:

It sure looks like aes_ctr (equivalent to :aes_ctr in Elixir) is one of the specific literal values that type can have.

But the formatting – for the aes_ctr value specifically, and only that value – is slightly different. Does that different formatting mean something relevant to this?

I did find this GitHub issue, and several related ones in the same project:

Their fix seems to do something like this with the new functions:

    key_length = bit_size(key)
    cipher     = "aes_#{key_length}_ctr" |> String.to_atom()

    :crypto.crypto_one_time( cipher, key, iv, data, encrypt: false )

I’m more curious about why the cipher_iv type in the crypto docs seem to indicate that :aes_ctr is or should be a perfectly valid value.

Indeed the odd formating is just due to whitespace and <br/>. Here is the source code:

-type cipher_iv() :: aes_128_cbc
                   | aes_192_cbc
                   | aes_256_cbc
                   | aes_cbc

                   | aes_128_cfb128
                   | aes_192_cfb128
                   | aes_256_cfb128
                   | aes_cfb128

                   | aes_128_cfb8
                   | aes_192_cfb8
                   | aes_256_cfb8
                   | aes_cfb8

                   | aes_128_ctr
                   | aes_192_ctr
                   | aes_256_ctr
                   | aes_ctr

                   | blowfish_cbc
                   | blowfish_cfb64
                   | blowfish_ofb64
                   | chacha20
                   | des_ede3_cbc
                   | des_ede3_cfb

                   | des_cbc
                   | des_cfb
                   | rc2_cbc .

Should be fine.

I cannot help you much, but maybe check the installed openssl version installed on the machine, in regard to the requirements of your version of OTP. Maybe it globally works but that precise algorithm is missing (as your error comes from a .c file).

1 Like

Thanks!

I opted for the ‘AES length ctr’ name trick, but you might be right that the problem might be the OpenSSL package on my computer. The crypto docs do state (right under the type info I referenced):

Note that this list might be reduced if the underlying libcrypto does not support all of them.

And maybe aes_ctr is something like ‘the default AES CTR cipher’ and my package doesn’t have one.

But I didn’t change my OpenSSL package and :aes_ctr worked with the previous code (before upgrading Erlang/OTP).

(There’s also a separate, but maybe related, mystery as to where crypto comes from, for my project. The Elixir getting started docs claim:

The :crypto module is not part of the Erlang standard library, but is included with the Erlang distribution. This means you must list :crypto in your project’s applications list whenever you use it.

But :crypto isn’t in the application list for this project. Maybe it’s in the Erlang/OTP package(s) we’re installing via asdf?

Well, crypto is probably coming from :plug_crypto! (Duh :slight_smile: )

You can look at cloak's source code for inspiration. They had a PR several weeks ago that was dealing exactly with migration to Erlang 24’s crypto.

1 Like

Thanks!

(That’s a nice little library BTW!)

This seems to be the relevant PR:

It seems like they ran into the same problem I had, i.e. :aes_ctr not working – or they switched to :aes_256_ctr for some other reason.

I’m still confused, but it’s a little comforting finding that others seem to have run into the same problem I had.

But :crypto isn’t in the application list for this project. Maybe it’s in the Erlang/OTP package(s) we’re installing via asdf?

:crypto is a part of and is distributed with Erlang/OTP, so yes

Your error message says crypto 4.8.3 - the value you mention was added as a helpful alias to help with transitioning from the removed old API to the new crypto api in OTP 24.0 (in crypto 5.0).

What is your Erlang/OTP version? Make sure to browse the Erlang/OTP documentation for your version. And are you fixing errors or just warnings?

1 Like

Thanks!

I’m still confused about the distinction between crypto being included in Erlang/OTP but NOT (supposedly) being part of “the Erlang standard library”. I just tested running some example code using that module in iex in a ‘scratchpad’ Elixir project I have – one with NO explicit deps – and the code ran just fine.

Do you know why the Elixir Getting Started guide states that :crypto should be added to a project’s :extra_applications key (for the application function in mix.exs)?

I’m still unclear as to why the :aes_ctr alias doesn’t seem to work – I didn’t change the system OpenSSL library.

I’m testing upgrading one project from Erlang/OTP 23.2.4 to 24.0.6. The project uses asdf to manage Erlang/OTP.

I am fixing ‘serious’ warnings, and definitely fixing any errors. I’m still curious as to why the :aes_ctr alias didn’t work – that was the only, or one of only a few, errors that I’ve seen since I started testing after upgrading Erlang/OTP.

OTP consists of multiple separate applications. One of them is :stdlib. :crypto is another. The reason for needing to explicitly state which of the otp apps you need is due to the fact that not all of the otp applications are included by erlang installations on different OSs/package managers. Sometimes certain applications of otp need to be explicitly installed on top of a base erlang installation to be available.

2 Likes

OTP consists of multiple separate applications. One of them is :stdlib .

Ha – that makes a lot of sense, but it seems (pleasantly) surprising that there’s an app with that name!

The rest of your explanation makes a lot of sense. This seems then like something that’s hard to appreciate, i.e. the benefits of explicitly listing these kinds of dependencies, as it’s very un-obvious how things work at all if this is seemingly required.

Thanks for the details!