libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
xtc_tls.h
1/*-
2 * Copyright (c) 2026, The XTC Project
3 * Use of this source code is governed by the ISC License,
4 * a copy of which is in the file LICENSE in the top-level directory
5 * of this distribution.
6 *
7 * src/inc/xtc_tls.h
8 * Transport Layer Security (TLS) API for xtc.
9 *
10 * Provides TLS 1.2/1.3 over xtc_io sockets with the same async,
11 * single-threaded event-loop discipline as the rest of xtc. The
12 * implementation is backend-pluggable at configure time via
13 * --with-tls=openssl|none|auto; the public API is identical
14 * regardless of the backend selected.
15 *
16 * Two opaque types:
17 * xtc_tls_ctx_t -- per-process context: loaded cert + key + CA bundle.
18 * xtc_tls_t -- per-connection state machine.
19 *
20 * Usage pattern:
21 *
22 * // 1. Create a shared context (once per server/client role):
23 * xtc_tls_opts_t opts = { .cert_file = "srv.crt", .key_file = "srv.key",
24 * .verify_peer = 0,
25 * .min_version = XTC_TLS_VER_12 };
26 * xtc_tls_ctx_t *ctx;
27 * xtc_tls_ctx_create(XTC_TLS_SERVER, &opts, &ctx);
28 *
29 * // 2. Wrap an accepted fd:
30 * xtc_tls_t *tls;
31 * xtc_tls_create(ctx, fd, &tls);
32 *
33 * // 3. Drive the non-blocking handshake inside the event loop:
34 * for (;;) {
35 * int rc = xtc_tls_handshake(tls);
36 * if (rc == XTC_OK) break;
37 * if (rc != XTC_E_AGAIN) { break; } // hard error: tear down
38 * uint32_t want = xtc_tls_wants_read(tls)
39 * ? XTC_IO_READABLE : XTC_IO_WRITABLE;
40 * xtc_io_mod_fd(io, fd, want, tls);
41 * // yield, await event ...
42 * }
43 *
44 * // 4. Encrypted I/O:
45 * size_t n;
46 * xtc_tls_read(tls, buf, sizeof(buf), &n);
47 * xtc_tls_write(tls, "hello", 5, &n);
48 *
49 * // 5. Graceful shutdown + cleanup:
50 * xtc_tls_shutdown(tls);
51 * xtc_tls_destroy(tls);
52 * xtc_tls_ctx_destroy(ctx);
53 *
54 * When TLS support is not compiled in (--with-tls=none), every
55 * function returns XTC_E_NOSYS; callers may test for that at
56 * runtime to skip TLS-dependent code paths.
57 */
58
59#ifndef XTC_TLS_H
60#define XTC_TLS_H
61
62#include <stddef.h>
63#include "xtc.h"
64
65/*
66 * XTC_TLS_ENABLED is defined (to 1) whenever a real TLS backend is
67 * compiled in -- any one of OpenSSL/LibreSSL, mbedTLS, GnuTLS, wolfSSL,
68 * or SChannel. It is NOT defined for --with-tls=none (the NOSYS
69 * stubs). Callers and tests use it to gate code that needs a working
70 * handshake without caring which backend provides it. The selecting
71 * XTC_TLS_BACKEND_* macro comes from xtc_config.h via configure.
72 */
73#if defined(XTC_TLS_BACKEND_OPENSSL) || \
74 defined(XTC_TLS_BACKEND_MBEDTLS) || \
75 defined(XTC_TLS_BACKEND_GNUTLS) || \
76 defined(XTC_TLS_BACKEND_WOLFSSL) || \
77 defined(XTC_TLS_BACKEND_SCHANNEL)
78#define XTC_TLS_ENABLED 1
79#endif
80
81#ifdef __cplusplus
82extern "C" {
83#endif
84
85/* -------------------------------------------------------------------------
86 * Opaque types.
87 * ----------------------------------------------------------------------- */
88
89/*
90 * xtc_tls_ctx_t -- per-process (or per-vhost) TLS context.
91 *
92 * Holds the loaded certificate, private key, and CA bundle. A
93 * single context may be shared by many concurrent connections of
94 * the same role; it is internally reference-safe via immutable
95 * configuration after creation.
96 */
97typedef struct xtc_tls_ctx xtc_tls_ctx_t;
98
99/*
100 * xtc_tls_t -- per-connection TLS state machine.
101 *
102 * Wraps an existing file descriptor. The fd remains owned by the
103 * caller; xtc_tls_destroy does not close it.
104 */
105typedef struct xtc_tls xtc_tls_t;
106
107/* -------------------------------------------------------------------------
108 * Role.
109 * ----------------------------------------------------------------------- */
110
111typedef enum xtc_tls_role {
112 XTC_TLS_SERVER = 0, /* accept connections, present certificate */
113 XTC_TLS_CLIENT = 1 /* initiate connections, optionally verify server */
114} xtc_tls_role_t;
115
116/* -------------------------------------------------------------------------
117 * TLS version constants.
118 * ----------------------------------------------------------------------- */
119
120#define XTC_TLS_VER_12 0x0303 /* TLS 1.2 (RFC 5246) */
121#define XTC_TLS_VER_13 0x0304 /* TLS 1.3 (RFC 8446) */
122
123/* -------------------------------------------------------------------------
124 * Options.
125 * ----------------------------------------------------------------------- */
126
127/*
128 * xtc_tls_opts_t -- creation-time parameters for xtc_tls_ctx_create.
129 *
130 * All string pointers are borrowed for the duration of the
131 * xtc_tls_ctx_create call only; the implementation copies any data
132 * it needs before returning.
133 *
134 * Fields:
135 * cert_file Path to the PEM certificate file. Required for
136 * SERVER role; ignored (may be NULL) for CLIENT role
137 * unless mutual TLS is needed.
138 *
139 * key_file Path to the PEM private-key file matching cert_file.
140 * Required when cert_file is set.
141 *
142 * ca_file Path to a PEM CA bundle used for peer verification.
143 * If NULL the backend's system CA bundle is used.
144 *
145 * verify_peer Boolean. 1 = require a valid peer certificate;
146 * 0 = do not verify. For SERVER role, 1 enables
147 * mutual TLS (client must present a cert).
148 *
149 * alpn_protos ALPN protocol list in wire encoding:
150 * "\x02h2\x08http/1.1". NULL disables ALPN
151 * negotiation.
152 *
153 * min_version Minimum TLS version to accept. Use XTC_TLS_VER_12
154 * or XTC_TLS_VER_13. 0 means "backend default"
155 * (typically TLS 1.2).
156 *
157 * max_version Maximum TLS version to offer. 0 means "backend
158 * default" (typically TLS 1.3).
159 */
160typedef struct xtc_tls_opts {
161 const char *cert_file;
162 const char *key_file;
163 const char *ca_file;
164 int verify_peer;
165 const char *alpn_protos;
166 int min_version;
167 int max_version;
169
170/* -------------------------------------------------------------------------
171 * Context lifecycle.
172 * ----------------------------------------------------------------------- */
173
174/*
175 * PUBLIC: int xtc_tls_ctx_create __P((xtc_tls_role_t,
176 * PUBLIC: const xtc_tls_opts_t *,
177 * PUBLIC: xtc_tls_ctx_t **));
178 * PUBLIC: void xtc_tls_ctx_destroy __P((xtc_tls_ctx_t *));
179 */
180
181/*
182 * xtc_tls_ctx_create --
183 * Allocate and initialise a TLS context.
184 *
185 * role SERVER or CLIENT.
186 * opts Options struct. May be NULL (all defaults).
187 * out On XTC_OK, *out points to the new context.
188 *
189 * Returns:
190 * XTC_OK on success
191 * XTC_E_INVAL if out is NULL or opts contains contradictory settings
192 * XTC_E_NOMEM on allocation failure
193 * XTC_E_NOSYS if TLS support was not compiled in
194 */
195int xtc_tls_ctx_create(xtc_tls_role_t role,
196 const xtc_tls_opts_t *opts,
197 xtc_tls_ctx_t **out);
198
199/*
200 * xtc_tls_ctx_destroy --
201 * Release all resources held by a TLS context.
202 * Must not be called while any xtc_tls_t created from it is live.
203 * ctx may be NULL (no-op).
204 */
205void xtc_tls_ctx_destroy(xtc_tls_ctx_t *ctx);
206
207/* -------------------------------------------------------------------------
208 * Per-connection lifecycle.
209 * ----------------------------------------------------------------------- */
210
211/*
212 * PUBLIC: int xtc_tls_create __P((xtc_tls_ctx_t *, int, xtc_tls_t **));
213 * PUBLIC: void xtc_tls_destroy __P((xtc_tls_t *));
214 */
215
216/*
217 * xtc_tls_create --
218 * Wrap an existing file descriptor in a TLS state machine.
219 *
220 * ctx The context created by xtc_tls_ctx_create.
221 * fd A non-blocking socket fd. Ownership remains with the caller;
222 * xtc_tls_destroy does not close fd.
223 * out On XTC_OK, *out points to the new xtc_tls_t.
224 *
225 * After this call the handshake has not yet run; call
226 * xtc_tls_handshake to drive it.
227 *
228 * Returns:
229 * XTC_OK on success
230 * XTC_E_INVAL if ctx or out is NULL, or fd < 0
231 * XTC_E_NOMEM on allocation failure
232 * XTC_E_NOSYS if TLS support was not compiled in
233 */
234int xtc_tls_create(xtc_tls_ctx_t *ctx, int fd, xtc_tls_t **out);
235
236/*
237 * xtc_tls_destroy --
238 * Release per-connection TLS state. Does not close the underlying fd,
239 * does not send close_notify -- call xtc_tls_shutdown first if a clean
240 * shutdown is needed.
241 * tls may be NULL (no-op).
242 */
243void xtc_tls_destroy(xtc_tls_t *tls);
244
245/* -------------------------------------------------------------------------
246 * Handshake.
247 * ----------------------------------------------------------------------- */
248
249/*
250 * PUBLIC: int xtc_tls_handshake __P((xtc_tls_t *));
251 * PUBLIC: int xtc_tls_wants_read __P((const xtc_tls_t *));
252 * PUBLIC: int xtc_tls_wants_write __P((const xtc_tls_t *));
253 */
254
255/*
256 * xtc_tls_handshake --
257 * Drive the TLS handshake state machine one step.
258 *
259 * Returns:
260 * XTC_OK handshake complete; the connection is ready for I/O
261 * XTC_E_AGAIN the fd is not yet ready; poll on
262 * xtc_tls_wants_read ? XTC_IO_READABLE
263 * : XTC_IO_WRITABLE
264 * and call again when the event fires
265 * XTC_E_INVAL tls is NULL
266 * XTC_E_NOSYS TLS not compiled in
267 * (other) backend-specific hard error; connection must be torn down
268 */
269int xtc_tls_handshake(xtc_tls_t *tls);
270
271/* -------------------------------------------------------------------------
272 * Encrypted I/O.
273 * ----------------------------------------------------------------------- */
274
275/*
276 * PUBLIC: int xtc_tls_read __P((xtc_tls_t *, void *, size_t, size_t *));
277 * PUBLIC: int xtc_tls_write __P((xtc_tls_t *, const void *, size_t, size_t *));
278 */
279
280/*
281 * xtc_tls_read --
282 * Read up to buflen decrypted bytes into buf.
283 *
284 * On XTC_OK, *out_n holds the number of bytes read (may be < buflen).
285 * On XTC_E_AGAIN the fd was not readable; *out_n is 0.
286 * On XTC_E_INVAL tls, buf, or out_n is NULL.
287 * On XTC_E_NOSYS TLS was not compiled in.
288 */
289int xtc_tls_read(xtc_tls_t *tls, void *buf, size_t buflen, size_t *out_n);
290
291/*
292 * xtc_tls_write --
293 * Encrypt and write up to buflen bytes from buf.
294 *
295 * On XTC_OK, *out_n holds the number of bytes consumed (may be < buflen).
296 * On XTC_E_AGAIN the fd was not writable; *out_n is 0.
297 * On XTC_E_INVAL tls, buf, or out_n is NULL.
298 * On XTC_E_NOSYS TLS was not compiled in.
299 */
300int xtc_tls_write(xtc_tls_t *tls, const void *buf, size_t buflen,
301 size_t *out_n);
302
303/* -------------------------------------------------------------------------
304 * Readiness queries.
305 * ----------------------------------------------------------------------- */
306
307/*
308 * xtc_tls_wants_read --
309 * Return non-zero if the most recent TLS operation stalled waiting
310 * for the underlying fd to become readable. The caller should arm
311 * a POLLIN/XTC_IO_READABLE watch and retry.
312 */
313int xtc_tls_wants_read(const xtc_tls_t *tls);
314
315/*
316 * xtc_tls_wants_write --
317 * Return non-zero if the most recent TLS operation stalled waiting
318 * for the underlying fd to become writable. The caller should arm
319 * a POLLOUT/XTC_IO_WRITABLE watch and retry.
320 */
321int xtc_tls_wants_write(const xtc_tls_t *tls);
322
323/* -------------------------------------------------------------------------
324 * Graceful shutdown.
325 * ----------------------------------------------------------------------- */
326
327/*
328 * PUBLIC: int xtc_tls_shutdown __P((xtc_tls_t *));
329 */
330
331/*
332 * xtc_tls_shutdown --
333 * Initiate or continue a TLS close_notify shutdown.
334 *
335 * Returns:
336 * XTC_OK shutdown complete; the underlying fd may be closed
337 * XTC_E_AGAIN not yet done; poll for readiness as with the handshake
338 * and call again
339 * XTC_E_INVAL tls is NULL
340 * XTC_E_NOSYS TLS not compiled in
341 */
342int xtc_tls_shutdown(xtc_tls_t *tls);
343
344#ifdef __cplusplus
345}
346#endif
347
348#endif /* XTC_TLS_H */