Skip to content

S26.01: Client-side session resumption (RFC 5077 tickets / session IDs) — OpenSSL backend #280

Description

@DavidCozens

Parent epic: #279 (E26: TLS Hardening)
Follow-on from: #276 (S12.14 — fail-fast streams + eager Service drain)
Sibling: mbedTLS backend parity tracked separately under E26.

Background

SolidSyslogTlsStream
(Platform/OpenSsl/Source/SolidSyslogTlsStream.c) currently creates
SSL_CTX_new(TLS_client_method()) with default options. OpenSSL's
client-side session cache is off by default.

Combined with S12.14's fail-fast reconnect model, every transient peer
hiccup now produces a fresh socket and a fresh TLS handshake. Each full
handshake costs ~2 RTTs and the asymmetric crypto on both sides. On a
flaky link this is the dominant cost of the reconnect loop and CPU on
embedded targets is already constrained.

Session resumption (RFC 5077 tickets, or session IDs) cuts the next
handshake to ~1 RTT and skips the asymmetric crypto.

Scope

  • Enable SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT).
  • Hook the new-session callback (SSL_CTX_sess_set_new_cb) so we hold
    one SSL_SESSION* per TlsStream instance and feed it via
    SSL_set_session(ssl, saved) on the next handshake.
  • Free the saved session on Close / Destroy (SSL_SESSION_free).
  • One session per stream is enough — we don't multiplex destinations
    inside a single Stream.
  • Graceful fallback. When no saved session exists, or the peer
    offers no ticket, or the saved session is expired/evicted, the stream
    must fall back to a full handshake and still deliver. Resumption is
    best-effort — never a delivery precondition.

Verification

Two tiers, matching the established TlsStream pattern:

  • Unit (fake-libssl, Tests/SolidSyslogTlsStreamTest.cpp): assert
    SSL_CTX_set_session_cache_mode is called with the client-cache flag;
    assert the new-session callback path stores a session pointer; assert
    SSL_set_session is called on the second connect of the same stream
    instance; assert the saved session is freed on Close / Destroy.
  • Integration (real libssl, Tests/OpenSslIntegration/SolidSyslogTlsStreamIntegrationTest.cpp,
    run as the integration-linux-openssl / integration-windows-openssl
    CI checks): stand up a server that offers tickets, connect, force a
    reconnect on the same stream (kill the peer / RST), and assert
    SSL_session_reused(ssl) == 1 on the second handshake. Add a
    no-ticket case asserting a clean full-handshake fallback that still
    delivers.

Note: this is not a BDD concern. SSL_session_reused() is an
in-process libssl call that the syslog-ng / Behave layer cannot
observe — the resumption assertion only exists in the integration
harness.

Out of scope / note

  • mbedTLS backend parity is a separate story under E26 — the two
    backends' work is independent.
  • Server-side session tickets are the peer's concern (we're a client).
  • TLS 1.3 0-RTT (early data) is deliberately not in scope —
    separate story under the same epic if/when there's demand.

Acceptance

  • Integration test forces a same-stream reconnect and observes
    SSL_session_reused(ssl) == 1 on the second handshake.
  • Integration test with a non-resuming peer falls back to a full
    handshake and still delivers the message.
  • Coverage stays at 100% line/branch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    storyStory issue

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions