DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
nts.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 nts.h
6 * @brief Network Time Security (NTS, RFC 8915) wire codec (DETWS_ENABLE_NTS).
7 *
8 * NTS secures NTP against spoofing. It has two wire formats, both codified here:
9 *
10 * - **NTS-KE** (Key Establishment, RFC 8915 sec 4), a short record exchange run over TLS 1.3 on port
11 * 4460: TLV records `[critical|type : u16][body-length : u16][body]`. The client offers a next
12 * protocol (NTPv4) + an AEAD algorithm (AES-SIV-CMAC-256); the server returns cookies + the
13 * negotiated AEAD (+ optional server/port). `detws_nts_ke_record` / `_request` build the request and
14 * `detws_nts_ke_parse` walks a response, surfacing each record via a callback.
15 *
16 * - **NTS-protected NTP** (RFC 8915 sec 5), NTPv4 with RFC 7822 extension fields: the Unique
17 * Identifier, the NTS Cookie, and the NTS Authenticator-and-Encrypted-Extension-Fields (AEAD nonce +
18 * ciphertext). `detws_nts_ef` builds a padded extension field; `detws_nts_ef_unique_id` /
19 * `_cookie` are the common ones.
20 *
21 * Pure framing, zero heap, no stdlib, host-testable. The AES-SIV-CMAC-256 AEAD (RFC 5297) that protects
22 * the authenticator, and the TLS-exporter key derivation (sec 5.1), are the crypto integration on top -
23 * the label constants for that derivation are exposed here.
24 */
25
26#ifndef DETERMINISTICESPASYNCWEBSERVER_NTS_H
27#define DETERMINISTICESPASYNCWEBSERVER_NTS_H
28
29#include "ServerConfig.h"
30#include <stddef.h>
31#include <stdint.h>
32
33#if DETWS_ENABLE_NTS
34
35/** @brief NTS-KE record types (RFC 8915 sec 4). The critical bit is 0x8000. */
36struct Nts
37{
38 static constexpr uint16_t NTS_KE_CRITICAL = 0x8000;
39 static constexpr uint16_t NTS_KE_END_OF_MESSAGE = 0;
40 static constexpr uint16_t NTS_KE_NEXT_PROTOCOL = 1;
41 static constexpr uint16_t NTS_KE_ERROR = 2;
42 static constexpr uint16_t NTS_KE_WARNING = 3;
43 static constexpr uint16_t NTS_KE_AEAD_ALGORITHM = 4;
44 static constexpr uint16_t NTS_KE_COOKIE = 5;
45 static constexpr uint16_t NTS_KE_NTPV4_SERVER = 6;
46 static constexpr uint16_t NTS_KE_NTPV4_PORT = 7;
47 static constexpr uint16_t NTS_NEXT_PROTO_NTPV4 = 0; ///< the only next-protocol defined.
48 static constexpr uint16_t NTS_AEAD_AES_SIV_CMAC_256 =
49 15; ///< the mandatory-to-implement AEAD (RFC 5297 / IANA id 15).
50};
51
52/** @brief NTS NTP extension-field types (RFC 8915 sec 5.3; RFC 7822 EF format). */
53struct NtsEf
54{
55 static constexpr uint16_t NTS_EF_UNIQUE_IDENTIFIER = 0x0104;
56 static constexpr uint16_t NTS_EF_COOKIE = 0x0204;
57 static constexpr uint16_t NTS_EF_COOKIE_PLACEHOLDER = 0x0304;
58 static constexpr uint16_t NTS_EF_AUTH_AND_ENCRYPTED = 0x0404;
59};
60
61/** @brief RFC 8915 sec 5.1 TLS exporter label + per-direction context (C2S = 0x0000_0001_00, S2C = ..01). */
62extern const char NTS_EXPORTER_LABEL[]; ///< "EXPORTER-network-time-security".
63
64/** @brief Build one NTS-KE record `[critical|type][len][body]`. @return bytes written, or 0 if it won't fit. */
65size_t detws_nts_ke_record(bool critical, uint16_t type, const uint8_t *body, size_t body_len, uint8_t *out,
66 size_t cap);
67
68/**
69 * @brief Build the standard NTS-KE client request: Next Protocol (NTPv4), AEAD (AES-SIV-CMAC-256), End
70 * of Message - all critical. @return bytes written, or 0 if @p cap is too small.
71 */
72size_t detws_nts_ke_request(uint8_t *out, size_t cap);
73
74/** @brief One record surfaced by detws_nts_ke_parse. */
75typedef void (*DetwsNtsKeCb)(bool critical, uint16_t type, const uint8_t *body, size_t body_len, void *arg);
76
77/**
78 * @brief Walk an NTS-KE record stream, invoking @p cb for each record.
79 * @return true if the stream is well-formed and ends with an End-of-Message record.
80 */
81bool detws_nts_ke_parse(const uint8_t *buf, size_t len, DetwsNtsKeCb cb, void *arg);
82
83/**
84 * @brief Build an RFC 7822 extension field `[type][length][value][padding-to-4]`.
85 * @param field_type the NTS_EF_* type.
86 * @param value the field value (may be null when value_len == 0).
87 * @param value_len value length.
88 * @return the total field length written (a multiple of 4), or 0 if it won't fit. The Length field
89 * counts the type + length + value + padding, per RFC 7822.
90 */
91size_t detws_nts_ef(uint16_t field_type, const uint8_t *value, size_t value_len, uint8_t *out, size_t cap);
92
93/** @brief Build a Unique Identifier EF (>= 32 bytes of the caller's random, RFC 8915 sec 5.3). */
94size_t detws_nts_ef_unique_id(const uint8_t *nonce, size_t nonce_len, uint8_t *out, size_t cap);
95
96/** @brief Build an NTS Cookie EF carrying @p cookie. */
97size_t detws_nts_ef_cookie(const uint8_t *cookie, size_t cookie_len, uint8_t *out, size_t cap);
98
99#endif // DETWS_ENABLE_NTS
100#endif // DETERMINISTICESPASYNCWEBSERVER_NTS_H
User-facing configuration for DeterministicESPAsyncWebServer.