TLS 1.3 handshake fails for Erlang/OTP clients (Akkoma/Pleroma link previews) – missing ChangeCipherSpec in middlebox compatibility mode

Hi,

I host my blog on Codefloe Pages (blog.jyrgi.de → juergen.codefloe.page, server header git-pages). Browsers and curl connect fine, but Erlang/OTP-based clients cannot complete the TLS 1.3 handshake. As a result, Fediverse servers built on Erlang/Elixir (Akkoma, Pleroma) can’t fetch link previews for any page hosted on Codefloe Pages.

Error on the client side (Akkoma log):

Description: 'Failed to assert middlebox server message'
Reason: [missing: {change_cipher_spec, 1}]
TLS client: In state hello_middlebox_assert at ssl_gen_statem.erl:807
generated CLIENT ALERT: Fatal - Unexpected Message
{unexpected_msg, {internal, {encrypted_extensions, ...}}}

What seems to happen: The Erlang client uses TLS 1.3 middlebox compatibility mode (RFC 8446, Appendix D.4) and sends a non-empty legacy_session_id. It then expects the server to send a dummy change_cipher_spec record right after the ServerHello. The Pages server goes straight to EncryptedExtensions without that record, and Erlang’s TLS stack aborts the handshake. Other clients are more lenient, which is probably why this hasn’t come up before.

Reproduce (any machine with Erlang/OTP installed):

bash

# fails:
erl -noshell -eval 'ssl:start(), io:format("~p~n", [ssl:connect("blog.jyrgi.de", 443, [{verify,verify_none},{server_name_indication,"blog.jyrgi.de"}], 5000)]), halt().'

# works (middlebox compatibility disabled on the client):
erl -noshell -eval 'ssl:start(), io:format("~p~n", [ssl:connect("blog.jyrgi.de", 443, [{verify,verify_none},{server_name_indication,"blog.jyrgi.de"},{middlebox_comp_mode,false}], 5000)]), halt().'

I’ve patched my own Akkoma instance as a workaround, but other Pleroma/Akkoma instances on the Fediverse will still fail to generate previews for Codefloe-hosted pages. Would it be possible to have the server (or the TLS terminator in front of it) send the dummy ChangeCipherSpec when the client offers a session ID?

Thanks for the great service!

Jürgen

Hi Jürgen,

Thanks for the detailed report.

CodeFloe Pages and codefloe.com sit behind HAProxy built against wolfSSL. The wolfSSL version we were running only sends the dummy ChangeCipherSpec from RFC 8446 Appendix D.4 when it’s compiled with a specific option, and our build didn’t have it. Turn out newer wolfSSL releases always send it on the server side, so all we needed to do was to upgrade to the latest version.

The fix is live. The server now sends the ChangeCipherSpec right after the ServerHello, and we’ve confirmed it against blog.jyrgi.de. Your test should now succeed without {middlebox_comp_mode,false}:

erl -noshell -eval 'ssl:start(), io:format("~p~n", [ssl:connect("blog.jyrgi.de", 443, [{verify,verify_none},{server_name_indication,"blog.jyrgi.de"}], 5000)]), halt().'

You can drop the workaround on your Akkoma instance.

Thanks again, and let us know if anything still fails.

Wow, that’s fast support. Thanks a lot :+1: