ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
quic_tls.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file pc_quic_tls.h
6 * @brief TLS 1.3 server handshake state machine for QUIC (RFC 9001 / RFC 8446).
7 *
8 * Drives the server side of the TLS 1.3 handshake that QUIC carries in CRYPTO frames. It ties the
9 * key schedule (pc_tls13_kdf), the handshake messages (pc_tls13_msg), and the transport parameters
10 * (pc_quic_tp) together: it runs the transcript hash, consumes the client's ClientHello, produces the
11 * server flight (ServerHello at the Initial level; EncryptedExtensions + Certificate +
12 * CertificateVerify + Finished at the Handshake level), derives the Handshake and 1-RTT packet keys
13 * for both directions, and verifies the client's Finished.
14 *
15 * The transport engine (pc_quic_conn) owns CRYPTO stream reassembly and packet protection; this module
16 * is transport-free. It consumes an in-order byte run (pc_quic_tls_recv_crypto returns how many bytes it
17 * used) and exposes the outbound flight per encryption level (pc_quic_tls_flight), so it is fully
18 * host-testable by feeding it a captured ClientHello and inspecting the flight and derived keys.
19 *
20 * Profile: TLS_AES_128_GCM_SHA256, X25519 (or the X25519MLKEM768 PQ/T hybrid when PC_ENABLE_PQC_KEX),
21 * Ed25519 certificate, no PSK / 0-RTT / client authentication. A client that offers X25519MLKEM768 but
22 * sends only a classical key_share is answered with a HelloRetryRequest (RFC 8446 sec 4.1.4) so it
23 * retries with the hybrid share instead of being downgraded to X25519. The ephemeral X25519 private key
24 * and the ServerHello random are supplied in the config (the caller draws them from its RNG, or fixes
25 * them in a test).
26 *
27 * @author Douglas Quigg (dstroy0)
28 * @date 2026
29 */
30
31#ifndef PROTOCORE_QUIC_TLS_H
32#define PROTOCORE_QUIC_TLS_H
33
34#include "protocore_config.h"
35
36#if PC_ENABLE_HTTP3
37
38#include "crypto/hash/sha256.h"
42#include <stddef.h>
43#include <stdint.h>
44
45/** @brief QUIC encryption levels (RFC 9001 sec 4). 0-RTT is not supported. */
46struct QuicEnc
47{
48 static constexpr uint8_t QUIC_ENC_INITIAL = 0;
49 static constexpr uint8_t QUIC_ENC_HANDSHAKE = 1;
50 static constexpr uint8_t QUIC_ENC_APP = 2; ///< 1-RTT (application) keys
51};
52
53/** @brief Server handshake configuration (certificate, key, transport params, ephemeral inputs). */
54struct QuicTlsConfig
55{
56 const uint8_t *cert_der; ///< DER X.509 leaf certificate (Ed25519 public key)
57 size_t cert_len;
58 uint8_t ed25519_seed[32]; ///< Ed25519 private seed matching the certificate
59 QuicTransportParams params; ///< the server's transport parameters (caller sets the CIDs)
60 uint8_t ephemeral_priv[32]; ///< server X25519 private key
61 uint8_t random[32]; ///< ServerHello random
62#if PC_ENABLE_PQC_KEX
63 uint8_t mlkem_m[32]; ///< ML-KEM Encaps randomness (X25519MLKEM768 hybrid); fresh per handshake
64#endif
65};
66
67/** @brief Handshake state (a mutually-exclusive internal state, not a wire value). */
68enum class QtlsState : uint8_t
69{
70 QTLS_START = 0, ///< awaiting ClientHello
71 QTLS_WAIT_FINISHED, ///< server flight sent; awaiting client Finished
72 QTLS_DONE, ///< client Finished verified
73 QTLS_FAILED, ///< a fatal handshake error (see alert)
74};
75
76/** @brief One server handshake's state (fixed storage, no heap). */
77struct QuicTls
78{
79 QuicTlsConfig cfg;
80 pc_sha256_ctx transcript; ///< running Transcript-Hash over the handshake messages
81 Tls13KeySchedule ks;
82
83 QtlsState state;
84 uint8_t alert; ///< TLS alert code (RFC 8446 sec 6) when state == QtlsState::QTLS_FAILED
85#if PC_ENABLE_PQC_KEX
86 bool hrr_sent; ///< a HelloRetryRequest was sent (X25519MLKEM768); the next ClientHello is the retry
87#endif
88 bool hs_keys_ready; ///< Handshake-level keys derived (after ServerHello)
89 bool ap_keys_ready; ///< 1-RTT keys derived (after the server Finished)
90 bool complete; ///< client Finished verified
91
92 QuicPacketKeys hs_client; ///< Handshake: opens client packets
93 QuicPacketKeys hs_server; ///< Handshake: seals server packets
94 QuicPacketKeys ap_client; ///< 1-RTT: opens client packets
95 QuicPacketKeys ap_server; ///< 1-RTT: seals server packets
96
97 uint8_t hs_finished_hash[32]; ///< H(ClientHello..server Finished), to verify client Finished
98
99#if PC_ENABLE_PQC_KEX
100 uint8_t flight_initial[1400]; ///< outbound Initial CRYPTO (ServerHello; hybrid key_share is ~1.1 KB)
101#else
102 uint8_t flight_initial[256]; ///< outbound Initial CRYPTO (ServerHello)
103#endif
104 size_t flight_initial_len;
105 uint8_t flight_hs[PC_H3_CRYPTO_BUF]; ///< outbound Handshake CRYPTO (EE..Finished)
106 size_t flight_hs_len;
107
108 QuicTransportParams peer; ///< the client's parsed transport parameters
109 bool have_peer;
110};
111
112/** @brief Initialize a server handshake with @p cfg (copied). Resets the transcript and state. */
113void pc_quic_tls_server_init(QuicTls *qt, const QuicTlsConfig *cfg);
114
115/**
116 * @brief Feed in-order CRYPTO stream bytes for encryption level @p level.
117 *
118 * Consumes as many complete handshake messages as @p data holds. At QuicEnc::QUIC_ENC_INITIAL it expects the
119 * ClientHello and, on success, builds the whole server flight and derives the Handshake + 1-RTT keys.
120 * At QuicEnc::QUIC_ENC_HANDSHAKE it expects the client Finished and verifies it. On a fatal error it sets the
121 * state to QtlsState::QTLS_FAILED and an alert. @return the number of leading bytes of @p data consumed (a
122 * partial trailing message is left for the next call).
123 */
124size_t pc_quic_tls_recv_crypto(QuicTls *qt, int level, const uint8_t *data, size_t len);
125
126/**
127 * @brief The pending outbound CRYPTO flight for @p level (QuicEnc::QUIC_ENC_INITIAL / QuicEnc::QUIC_ENC_HANDSHAKE).
128 * @return a pointer to the flight bytes and its length via @p len (0 if none). The transport engine
129 * fragments these into CRYPTO frames and tracks its own send offset / retransmission.
130 */
131const uint8_t *pc_quic_tls_flight(const QuicTls *qt, int level, size_t *len);
132
133/**
134 * @brief The packet-protection keys for @p level (QuicEnc::QUIC_ENC_HANDSHAKE / QuicEnc::QUIC_ENC_APP), @p is_server
135 * picking the seal (server) or open (client) direction. @return NULL if those keys are not ready.
136 */
137QuicPacketKeys *pc_quic_tls_keys(QuicTls *qt, int level, bool is_server);
138
139/** @brief The client's parsed transport parameters (valid once the ClientHello is processed). */
140const QuicTransportParams *pc_quic_tls_peer_params(const QuicTls *qt);
141
142#endif // PC_ENABLE_HTTP3
143#endif // PROTOCORE_QUIC_TLS_H
User-facing configuration for ProtoCore.
#define PC_H3_CRYPTO_BUF
Maximum bytes of one QUIC/TLS handshake CRYPTO flight (RFC 9001).
SHA-256 (FIPS 180-4) - streaming context and one-shot API.
Streaming SHA-256 context.
Definition sha256.h:40
TLS 1.3 key schedule (RFC 8446 sec 7.1) for the QUIC handshake.