Is it possible to seed the :crypto module?

Is it possible to seed the Erlang :crypto module? I expected this to be reproducible:

iex(1)> :crypto.rand_seed("42")
:ok
iex(2)> :crypto.strong_rand_bytes(2)
<<255, 203>>
iex(3)> :crypto.rand_seed("42")
:ok
iex(4)> :crypto.strong_rand_bytes(2)
<<81, 186>>

I expected both calls to :crypto.strong_rand_bytes(2) to return the same binary but that is obviously not the case. Is there some other way to seed the :crypto module?

From what I can see this is not possible. crypto uses OpenSSL under the hood and OpenSSL mixes the seed with unpredictable data.

It seems the the underlying RAND_seed function in OpenSSL actually calls RAND_add. (see man RAND_seed). According to :crypto.rand_seed documentation it is only meant to be used when you get :low_entropy back from the strong_rand_bytes function.

If this is for testing another way to structure your code is to pass a “random” function to you code and replace that function with a dummy during testing.

I’ve been working on a system that auto-generates parts of our docs, primarily requests and responses. Since the responses contain new ID’s generated with Ecto.UUID.generate/0 the contents of the responses change each time I generate the docs.

Since it is not possible/straightforward to seed the :crypto module I may need to to stub/mock out how the UUID’s are generated. For any UUID’s generated within the tests that would be relatively easy (since I have a uuid/0 function that just delegates to Ecto.UUID.generate/0 but I’d prefer not to have to change the actual implementation if possible. I also have to double-check if the UUID’s are being generated by Ecto or by Postgres.

As I recall strong random from :crypto pulls from OpenSSL, which pulls from local system entropy, which is definitely not reproduceable in such a way, you’d be looking at the normal random modules instead for reproduceability.

1 Like