xtc_tls(3)

---

xtc_tls(3)

TLS over async sockets

XTC_TLS(3) Library Functions Manual XTC_TLS(3)

xtc_tls_ctx_create, xtc_tls_ctx_destroy, xtc_tls_create, xtc_tls_create_transport, xtc_tls_ctx_set_sni_cb, xtc_tls_set_hostname, xtc_tls_destroy, xtc_tls_handshake, xtc_tls_read, xtc_tls_write, xtc_tls_shutdown, xtc_tls_wants_read, xtc_tls_wants_write, xtc_tls_get_version, xtc_tls_get_cipher, xtc_tls_get_cipher_bits, xtc_tls_get_alpn_selected, xtc_tls_has_peer_cert, xtc_tls_get_verify_error, xtc_tls_get_peer_subject_dn, xtc_tls_get_peer_common_name, xtc_tls_get_peer_issuer_dn, xtc_tls_get_peer_serial, xtc_tls_get_server_cert_hashTLS over async sockets

#include <xtc.h>
#include <xtc_tls.h>

xtc_tls provides TLS 1.2 / 1.3 over xtc_io sockets. The implementation selects a backend at configure time (‘--with-tls=openssl|none|auto’); in v0.x only OpenSSL is implemented.

:

xtc_tls_ctx_t
Per-process state: cert, key, CA bundle, version range, ALPN profile. Created once via () with role (XTC_TLS_SERVER or XTC_TLS_CLIENT) plus opts.
xtc_tls_t
Per-connection state. Wraps a single fd. Created via (ctx, fd, &out).

:

xtc_tls_create(ctx, fd, &tls);
for (;;) {
    int rc = xtc_tls_handshake(tls);
    if (rc == XTC_OK) break;
    if (rc != XTC_E_AGAIN) abort();
    uint32_t want = xtc_tls_wants_read(tls)
                  ? XTC_IO_READABLE : XTC_IO_WRITABLE;
    xtc_proc_wait_fd(fd, want, -1, &revents);
}

() and () follow the same pattern: return XTC_OK on progress, XTC_E_AGAIN when the fd needs more poll iterations.

() performs the bidirectional close-notify dance; same WANT_* translation.

xtc_tls_opts_t fields:

cert_file, key_file
PEM paths for server-side certificate.
ca_file
PEM CA bundle for verifying the peer.
verify_peer
to require + validate the peer's cert.
verify_peer_mode
Tri-state peer verification, taking precedence over verify_peer when not XTC_TLS_VERIFY_DEFAULT: XTC_TLS_VERIFY_NONE, XTC_TLS_VERIFY_REQUEST (request a client certificate but complete the handshake if none is presented), or XTC_TLS_VERIFY_REQUIRE.
alpn_protos
ALPN wire-form list.
min_version, max_version
or XTC_TLS_VER_13.
cipher_list, ciphersuites_13, groups
TLS 1.2 cipher list, TLS 1.3 ciphersuites, and key-exchange groups.
crl_file, crl_dir
CRL file / hashed directory; either enables full-chain revocation checking.
prefer_server_ciphers
Honor the server's cipher order over the client's.
passphrase_cb, passphrase_userdata
Supply the passphrase for an encrypted key_file without an interactive prompt.

A server context is hardened by default (documented defaults, not knobs): TLS renegotiation and compression are disabled, session tickets and the session cache are off, and the moving-write-buffer mode required for correct non-blocking sends is enabled.

(ctx, cb, userdata) installs a Server Name Indication selection callback on a XTC_TLS_SERVER context. During the handshake, after the ClientHello is parsed, the callback fires with the requested server_name (or NULL if the client sent none) and may return a DIFFERENT, pre-created server xtc_tls_ctx_t whose certificate, key, CA, and verify mode are used for the rest of that handshake -- the multi-tenant / multi-host TLS case (mirrors OpenSSL's () plus ()). Returning NULL keeps the current context. Registering on a client context, or on a backend that cannot swap mid-handshake, returns XTC_E_NOSYS.

(tls, name) is the client-side half: called before the handshake on a client connection, it sends name in the ClientHello SNI extension and enables RFC 6125 hostname verification against the server certificate. A NULL or empty name clears it; it is a no-op on a server connection.

(ctx, transport, &out) wraps the TLS state machine around caller-supplied read_cb and write_cb callbacks instead of an fd, so the consumer owns the underlying recv/send -- to feed bytes already read off the socket before deciding to negotiate TLS (pushback), to weave the I/O into its own interrupt / cancellation / fiber-yield discipline, or to handle a platform signal window. The callbacks are BIO-like: return the byte count, 0 for a clean EOF, or XTC_E_AGAIN to signal would-block (the caller then drives its own readiness and retries the TLS op). These are OpenSSL-backend features; other backends return XTC_E_NOSYS.

Valid only after () returns XTC_OK. () and () return the negotiated protocol version and cipher name (or NULL); () the symmetric key strength; () the negotiated ALPN protocol. () reports whether the peer presented a certificate that passed verification; () reports the most recent peer-certificate verification result for diagnostics: the backend verify code (OpenSSL X509_V_*, X509_V_OK == 0 on success) into *x509_err and a human-readable reason string into buf; (), (), (), and () read the peer certificate (RFC 2253 DN, commonName, issuer DN, decimal serial), rejecting an embedded NUL with XTC_E_INVAL. () returns the RFC 5929 hash of the local certificate for SCRAM channel binding (MD5/SHA-1 signatures are hashed with SHA-256, otherwise the digest matching the certificate's signature algorithm). A backend that has not implemented these returns NULL, 0, or XTC_E_NOSYS.

xtc_io(3), xtc_net(3), xtc_proc(3), xtc(7)

Appeared in xtc 0.1. See docs/M_TLS.md for backend pluggability planning.

May 28, 2026 Debian

View the mdoc source