ssl:connect/2,3 has no support for {verify, verify_peer} on the client side — process_options/3 crashes with function_clause

Open
#2,401 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 3 days

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
48/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Quiet
Tech stack
embedded-iot, erlang

Research direction

Start in libs/estdlib/src/ssl.erl, where the issue identifies process_options/3 and the tls_client_option() type. Read the client connection option handling and its tests, then check how certificate authorities are configured in this project. Done means verify_peer either verifies the peer against a supplied CA or returns a clear unsupported option error instead of crashing.

Written by the indexing model from the issue text.

Description

Summary

libs/estdlib/src/ssl.erl's client-connect path only supports
{verify, verify_none}. Passing {verify, verify_peer} — the option that
actually asks for certificate verification against a trust store — crashes
the calling process with function_clause, since process_options/3 has no
clause for it:

-spec process_options(
    SSLContext :: sslcontext(), SSLConfig :: sslconfig(), TLSOptions :: [tls_client_option()]
) -> ok.
process_options(_SSLContext, _SSLConfig, []) ->
    ok;
process_options(SSLContext, SSLConfig, [{server_name_indication, disabled} | Tail]) ->
    process_options(SSLContext, SSLConfig, Tail);
process_options(SSLContext, SSLConfig, [{server_name_indication, Hostname} | Tail]) ->
    ok = ?MODULE:nif_set_hostname(SSLContext, Hostname),
    process_options(SSLContext, SSLConfig, Tail);
process_options(SSLContext, SSLConfig, [{verify, verify_none} | Tail]) ->
    ok = ?MODULE:nif_conf_authmode(SSLConfig, none),
    process_options(SSLContext, SSLConfig, Tail);
process_options(SSLContext, SSLConfig, [{binary, true} | Tail]) ->
    process_options(SSLContext, SSLConfig, Tail);
process_options(SSLContext, SSLConfig, [binary | Tail]) ->
    process_options(SSLContext, SSLConfig, Tail);
process_options(SSLContext, SSLConfig, [{active, false} | Tail]) ->
    process_options(SSLContext, SSLConfig, Tail).

The module's own type spec agrees this is intentional, not an oversight —
verify_peer simply isn't a valid value yet:

-type tls_client_option() ::
    {server_name_indication, sni()} | {verify, verify_none}.
Reproduction
{ok, Socket} = socket:open(inet, stream, tcp),
ok = socket:connect(Socket, #{family => inet, addr => ..., port => 443}),
ssl:connect(Socket, [{active, false}, {verify, verify_peer}]).

crashes with:

{updater_crashed, {function_clause, []}}

(stacktrace comes back empty — AtomVM doesn't currently capture one for this
kind of crash, which made this harder to pin down than it should have been;
a {error, {unsupported_option, {verify, verify_peer}}} return, or at least
a non-empty stacktrace, would help here.)

Where this actually bit us

nerves_hub_link_atomvm_esp32's
OTA downloader (nh_ota.erl) connects to the firmware URL with
ahttp_client:connect(https, Host, Port, [{active, false}, {verify, verify_peer}]),
intending real certificate verification of the download host (a signed
firmware payload doesn't need it, but it's defense in depth). That call
crashes every OTA attempt with exactly this function_clause. We've patched
it locally to verify_none to keep testing (the firmware payload is still
checksummed independently, so this isn't a security hole for that project,
just a missing layer), but verify_peer is the option anyone would reach
for first, and AtomVM's own ssl.erl type spec says it should exist.

Environment
  • AtomVM: main @ 0220c78e (v0.7.0-alpha.1-211-g0220c78e)
  • Target: esp32 (ESP32-D0WDQ6, classic dual-core, no PSRAM)
  • ESP-IDF: v5.5.2
What would help

Either:

  • Implement {verify, verify_peer} against a supplied CA (cacerts/
    cacertfile-style option), matching the type spec that already promises
    it, or
  • If it's not implemented yet on purpose, update the type spec/docs to say
    so explicitly and return a clear {error, ...} instead of a bare
    function_clause with no stacktrace, so it fails obviously rather than
    looking like a caller bug.

Happy to help test a patch against real hardware — this came up debugging a
live NervesHub OTA update on an ESP32.

Dominant language
Erlang
Stars
2.3k
Forks
171
Avg merge
4d 11h
Merged PRs (30d)
20

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from atomvm/AtomVM

All issues in atomvm/AtomVM

Similar issues

More Networking issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.