DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
tls13_msg.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 tls13_msg.h
6 * @brief TLS 1.3 handshake messages for the QUIC handshake (RFC 8446 sec 4).
7 *
8 * The wire formats of the handshake messages a QUIC server exchanges: it parses the client's
9 * ClientHello and builds its own flight (ServerHello, then the encrypted EncryptedExtensions,
10 * Certificate, CertificateVerify, and Finished). Every message is emitted whole, including the
11 * 4-byte handshake header (msg_type + 24-bit length), because that is what both the CRYPTO stream
12 * carries and the transcript hash covers.
13 *
14 * This server is deliberately a single, spec-valid profile: cipher suite TLS_AES_128_GCM_SHA256,
15 * key share X25519, and an Ed25519 certificate (the only signature scheme we produce - the in-tree
16 * crypto has Ed25519 but no ECDSA P-256 or RSA-PSS). A ClientHello that offers none of these is a
17 * handshake failure, decided by the state machine that drives this module. QUIC transport parameters
18 * ride in the quic_transport_parameters extension (codepoint 0x39, RFC 9001 sec 8.2).
19 *
20 * Pure, zero heap, host-tested against the RFC 8448 sec 3 ServerHello / Certificate / Finished bytes
21 * and by ClientHello field extraction + an Ed25519 CertificateVerify sign/verify round-trip.
22 *
23 * @author Douglas Quigg (dstroy0)
24 * @date 2026
25 */
26
27#ifndef DETERMINISTICESPASYNCWEBSERVER_TLS13_MSG_H
28#define DETERMINISTICESPASYNCWEBSERVER_TLS13_MSG_H
29
30#include "ServerConfig.h"
31
32// Shared by the HTTP/3 (QUIC) handshake and the DTLS 1.3 handshake: both carry the same TLS 1.3
33// messages, so this module compiles for either. The DTLS-specific additions (HelloRetryRequest, the
34// cookie extension, the sec 4.4.1 message_hash) are used by the DTLS handshake but are valid TLS 1.3.
35#if (DETWS_ENABLE_HTTP3 || DETWS_ENABLE_DTLS)
36
37#include <stddef.h>
38#include <stdint.h>
39
40/** @brief TLS handshake message types (RFC 8446 sec 4). */
41struct TlsHs
42{
43 static constexpr uint8_t TLS_HS_CLIENT_HELLO = 1;
44 static constexpr uint8_t TLS_HS_SERVER_HELLO = 2;
45 static constexpr uint8_t TLS_HS_ENCRYPTED_EXTENSIONS = 8;
46 static constexpr uint8_t TLS_HS_CERTIFICATE = 11;
47 static constexpr uint8_t TLS_HS_CERTIFICATE_VERIFY = 15;
48 static constexpr uint8_t TLS_HS_FINISHED = 20;
49};
50
51#define TLS_CIPHER_AES_128_GCM_SHA256 0x1301 ///< the one cipher suite we support
52#define TLS_GROUP_X25519 0x001d ///< the classical key-exchange group we support
53#define TLS_GROUP_X25519MLKEM768 0x11ec ///< PQ/T hybrid group (ML-KEM-768 + X25519), when DETWS_ENABLE_PQC_KEX
54#define TLS_SIG_ED25519 0x0807 ///< the one signature scheme we produce
55#define TLS_VERSION_1_3 0x0304 ///< supported_versions selected value (TLS 1.3)
56static constexpr uint16_t TLS_VERSION_DTLS_1_3 = 0xFEFC; ///< supported_versions selected value (DTLS 1.3, RFC 9147)
57static constexpr uint16_t TLS_LEGACY_VERSION_DTLS = 0xFEFD; ///< legacy_version on the wire for DTLS (DTLS 1.2)
58#define TLS_EXT_QUIC_TRANSPORT_PARAMS 0x0039 ///< quic_transport_parameters (RFC 9001 sec 8.2)
59
60/** @brief What the state machine needs out of a parsed ClientHello (pointers alias the input). */
61struct Tls13ClientHello
62{
63 const uint8_t *session_id; ///< legacy_session_id (echoed back in ServerHello)
64 uint8_t session_id_len;
65 uint8_t client_x25519[32]; ///< the client's X25519 key_share (valid iff has_key_share or has_hybrid_share)
66 bool has_key_share;
67#if DETWS_ENABLE_PQC_KEX
68 bool offers_x25519mlkem768; ///< supported_groups contains X25519MLKEM768
69 bool has_hybrid_share; ///< key_share carried an X25519MLKEM768 entry
70 const uint8_t *client_mlkem_ek; ///< the client's ML-KEM-768 encapsulation key (1184 B, aliases input)
71#endif
72 bool offers_tls13; ///< supported_versions contains 0x0304
73 bool offers_x25519; ///< supported_groups contains x25519
74 bool offers_ed25519; ///< signature_algorithms contains ed25519
75 bool offers_h3_alpn; ///< ALPN contains "h3"
76 const uint8_t *quic_tp; ///< raw quic_transport_parameters extension body (or NULL)
77 size_t quic_tp_len;
78 const uint8_t *sni; ///< first server_name host_name (or NULL), not NUL-terminated
79 size_t sni_len;
80 const uint8_t *cookie; ///< cookie extension body echoed after a HelloRetryRequest (or NULL); DTLS §5.1
81 size_t cookie_len;
82 bool has_conn_id; ///< the connection_id extension was present (RFC 9146 / RFC 9147 §9)
83 const uint8_t *conn_id; ///< the CID the client wants the server to use in records sent to it (may be empty)
84 size_t conn_id_len; ///< length of @c conn_id (0..255; 0 = the client wants a zero-length CID)
85};
86
87/**
88 * @brief Parse a ClientHello handshake message (@p msg includes the 4-byte handshake header).
89 *
90 * @param dtls true for a DTLS ClientHello (RFC 9147 §5.3), which carries an extra @c legacy_cookie
91 * field between @c legacy_session_id and @c cipher_suites; false for TLS/QUIC.
92 * @return false if it is not a well-formed ClientHello. Missing/!supported extensions are reported
93 * through the offers_* flags rather than failing the parse, so the caller can send the right alert.
94 */
95bool tls13_parse_client_hello(const uint8_t *msg, size_t len, Tls13ClientHello *out, bool dtls = false);
96
97/**
98 * @brief Build a ServerHello (RFC 8446 sec 4.1.3) selecting TLS 1.3 / AES-128-GCM-SHA256 and a
99 * key_share for @p group.
100 *
101 * @param random 32-byte server random.
102 * @param session_id legacy_session_id_echo (the client's, echoed verbatim; may be NULL if len 0).
103 * @param session_id_len echoed session-id length (0..32).
104 * @param share the server's key_share (X25519 pub for the classical group, or the
105 * ciphertext || X25519 concatenation for X25519MLKEM768).
106 * @param share_len length of @p share (32 for X25519, 1120 for the hybrid).
107 * @param group the selected named group (default TLS_GROUP_X25519).
108 * @param conn_id when non-NULL, emit a connection_id extension (RFC 9146 / RFC 9147 §9)
109 * carrying the server's CID (the id the client must place in records it sends).
110 * @param conn_id_len length of @p conn_id (0..255).
111 * @return bytes written, or 0 on overflow.
112 */
113size_t tls13_build_server_hello(uint8_t *out, size_t cap, const uint8_t random[32], const uint8_t *session_id,
114 uint8_t session_id_len, const uint8_t *share, size_t share_len = 32,
115 uint16_t group = TLS_GROUP_X25519, bool dtls = false, const uint8_t *conn_id = nullptr,
116 size_t conn_id_len = 0);
117
118/**
119 * @brief Build EncryptedExtensions (RFC 8446 sec 4.3.1) carrying ALPN "h3" and the server's
120 * quic_transport_parameters (@p quic_tp, from quic_tp_encode). @return bytes written, or 0.
121 */
122size_t tls13_build_encrypted_extensions(uint8_t *out, size_t cap, const uint8_t *quic_tp, size_t quic_tp_len);
123
124/**
125 * @brief Build a Certificate message (RFC 8446 sec 4.4.2) with an empty request context and one
126 * CertificateEntry wrapping @p cert_der (DER X.509) with no entry extensions. @return bytes written.
127 */
128size_t tls13_build_certificate(uint8_t *out, size_t cap, const uint8_t *cert_der, size_t cert_len);
129
130/**
131 * @brief Build a CertificateVerify (RFC 8446 sec 4.4.3) with an Ed25519 signature.
132 *
133 * Signs the sec 4.4.3 content - 64 * 0x20, the context string "TLS 1.3, server CertificateVerify",
134 * a 0x00 separator, then @p transcript_hash (Transcript-Hash of ClientHello..Certificate) - with the
135 * Ed25519 key @p seed, and emits algorithm=ed25519, the 64-byte signature.
136 *
137 * @param transcript_hash 32-byte Transcript-Hash through the Certificate message.
138 * @param seed 32-byte Ed25519 private seed.
139 * @return bytes written, or 0 on overflow.
140 */
141size_t tls13_build_cert_verify(uint8_t *out, size_t cap, const uint8_t transcript_hash[32], const uint8_t seed[32]);
142
143/**
144 * @brief Build a Finished message (RFC 8446 sec 4.4.4) carrying @p verify_data (from
145 * tls13_finished_mac). @return bytes written, or 0 on overflow.
146 */
147size_t tls13_build_finished(uint8_t *out, size_t cap, const uint8_t verify_data[32]);
148
149/**
150 * @brief Assemble the sec 4.4.3 signed content into @p out (64*0x20 || context || 0x00 || hash).
151 *
152 * Exposed so the state machine can also verify a client's CertificateVerify if client auth is ever
153 * added, and so it is directly unit-testable. @p is_server picks the "server"/"client" context word.
154 * @return content length written (always 98 + 32), or 0 on overflow.
155 */
156size_t tls13_cert_verify_content(uint8_t *out, size_t cap, const uint8_t transcript_hash[32], bool is_server);
157
158// ---------------------------------------------------------------------------
159// HelloRetryRequest + cookie (RFC 8446 §4.1.4), used by the DTLS 1.3 handshake
160// ---------------------------------------------------------------------------
161
162/** @brief The fixed HelloRetryRequest random - SHA-256("HelloRetryRequest"), RFC 8446 §4.1.3. A
163 * ServerHello carrying this random _is_ a HelloRetryRequest. 32 bytes. */
164extern const uint8_t tls13_hrr_random[32];
165
166/**
167 * @brief Build a HelloRetryRequest (RFC 8446 §4.1.4): a ServerHello whose random is
168 * @ref tls13_hrr_random, selecting TLS 1.3 / AES-128-GCM-SHA256, asking the client to retry with a
169 * key_share for @p selected_group, and echoing @p cookie in the cookie extension (§4.2.2).
170 *
171 * @param session_id legacy_session_id_echo (the client's, echoed verbatim; may be NULL if len 0).
172 * @param selected_group the NamedGroup the server wants the client's key_share for.
173 * @param cookie the return-routability cookie the client must echo (may be NULL if len 0).
174 * @param dtls true to emit the DTLS 1.3 version codepoints (0xFEFD / 0xFEFC, RFC 9147 §5.3);
175 * false for the TLS 1.3 ones (0x0303 / 0x0304).
176 * @return bytes written, or 0 on overflow.
177 */
178size_t tls13_build_hello_retry_request(uint8_t *out, size_t cap, const uint8_t *session_id, uint8_t session_id_len,
179 uint16_t selected_group, const uint8_t *cookie, size_t cookie_len,
180 bool dtls = false);
181
182/**
183 * @brief Build an EncryptedExtensions (RFC 8446 §4.3.1) with an empty extension list - the DTLS
184 * profile carries no ALPN or transport parameters. @return bytes written, or 0 on overflow.
185 */
186size_t tls13_build_encrypted_extensions_empty(uint8_t *out, size_t cap);
187
188/**
189 * @brief Write the synthetic @c message_hash handshake message that replaces ClientHello1 in the
190 * transcript when a HelloRetryRequest is used (RFC 8446 §4.4.1): @c message_hash (254), a 24-bit
191 * length of 32, then @p ch1_hash. @return bytes written (36), or 0 on overflow.
192 */
193size_t tls13_build_message_hash(uint8_t *out, size_t cap, const uint8_t ch1_hash[32]);
194
195#endif // DETWS_ENABLE_HTTP3 || DETWS_ENABLE_DTLS
196#endif // DETERMINISTICESPASYNCWEBSERVER_TLS13_MSG_H
User-facing configuration for DeterministicESPAsyncWebServer.