:SSL Middlebox Compatibility Issues with FIPS Mode, mix deps.get fails

Hello!

I’m working on a piece of software with the requirement that I use a FIPS-approved encryption module. I’ve built OpenSSL 3.6.0 with 3.1.2 as a FIPS provider:

$ openssl list --providers
Providers:
  fips
    name: OpenSSL FIPS Provider
    version: 3.1.2
    status: active

Erlang has also been built with FIPS enabled:

Erlang/OTP 28 [erts-16.2] [source] [64-bit] [smp:20:20] [ds:20:20:10] [async-threads:1] [jit:ns]

Eshell V16.2 (press Ctrl+G to abort, type help(). for help)
1> application:load({application, crypto, [{env, [{fips_mode, true}]}]}), application:start(crypto).
ok
2> crypto:info_fips().
enabled

The problem is that mix deps.get refuses to work, and I kinda feel like it’s a misconfiguration of repo.hex.pm, but I really have no idea. I can successfully make requests to it as long as I manually pass middlebox_comp_mode: false, but there’s I don’t know if it’s even possible to do that with things like mix and libraries I need to use.

Nrap is just to test this issue, no other code except what’s included in a default mix project

mix.exs

defmodule Nrap.MixProject do
  use Mix.Project

  def project do
    [
      app: :nrap,
      version: "0.1.0",
      elixir: "~> 1.19",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  # Run "mix help compile.app" to learn about applications.
  def application do
    [
      extra_applications: [:logger]
    ]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
    {:req, "~> 0.5.16"}
    ]
  end
end

config/config.exs

import Config

config :crypto,
  fips_mode: true

$ mix deps.get

Failed to fetch record for telemetry from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for finch from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for hpax from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for mint from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for mime from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for nimble_options from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for req from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for nimble_pool from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for jason from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Resolving Hex dependencies...
Failed to fetch record for plug from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for brotli from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for ezstd from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for nimble_csv from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for decimal from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}
Failed to fetch record for castore from registry (using cache instead)
{:failed_connect, [{:to_address, {~c"repo.hex.pm", 443}}, {:inet, [:inet], {:options, {:insufficient_crypto_support, {:"tlsv1.1", {:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}}}}}]}

iex -S mix

Breaks added for readability

Erlang/OTP 28 [erts-16.2] [source] [64-bit] [smp:20:20] [ds:20:20:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.19.4) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> Req.get("https://google.com")

11:17:11.770 [debug] redirecting to https://www.google.com/
{:ok,
 %Req.Response{
   status: 200,
   ...
 }}
iex(2)> Req.get("https://repo.hex.pm")

11:18:00.602 [warning] Description: ~c"Failed to assert middlebox server message"
     Reason: [missing: {:change_cipher_spec, 1}]
   Location: tls_client_connection_1_3.erl:344


11:18:00.604 [notice] TLS :client: In state :hello_retry_middlebox_assert at ssl_gen_statem.erl:755 generated CLIENT ALERT: Fatal - Unexpected Message
 - {:unexpected_msg,
 {:internal,
  {:server_hello, {3, 3},
   <<...>>,
   <<...>>,
   <<...>>,
   %{
     server_hello_selected_version: {:server_hello_selected_version, {3, 4}},
     pre_shared_key: :undefined,
     key_share: {:key_share_server_hello,
      {:key_share_entry, :secp384r1,
       <<...>>}}
   }}}}
{:error,
 %Req.TransportError{
   reason: {:tls_alert,
    {:unexpected_message,
     ~c"TLS client: In state hello_retry_middlebox_assert at ssl_gen_statem.erl:755 generated CLIENT ALERT: Fatal - Unexpected Message\n {unexpected_msg,\n     {internal,\n         {server_hello,\n             {3,3},\n             <<...>>,\n             <<...>>,\n             <<19,1>>,\n             \#{server_hello_selected_version =>\n                   {server_hello_selected_version,{3,4}},\n               pre_shared_key => undefined,\n               key_share =>\n                   {key_share_server_hello,\n                       {key_share_entry,secp384r1,<<...>>}}}}}}"}}
iex(3)> Req.get("https://repo.hex.pm", connect_options: [transport_opts: [middlebox_comp_mode: false]])
{:ok,
 %Req.Response{
   status: 200,
   headers: %{
     "connection" => ["keep-alive"],
     "content-type" => ["text/plain; charset=utf8"],
     "date" => ["Tue, 16 Dec 2025 17:19:54 GMT"],
     "x-served-by" => ["cache-chi-kigq8000103-CHI"]
   },
   body: "Everything's Okay",
   trailers: %{},
   private: %{}
 }}

Some related issues that I haven’t been able to pull anything useful from:

Sorry if this is too much!

I would really appreciate any help on this, I’m a bit stuck :melting_face:

edit:

On further review this probably isnt relevant to us after all, as SC.L2-3.13.11 does not require the application to use FIPS-approved modules internally. I hope this helps some other poor soul out there.

1 Like