rama_net/tls/client/hello.rs
1use serde::{Deserialize, Serialize};
2use std::fmt::Display;
3
4use crate::address::Host;
5use crate::tls::enums::{
6 AuthenticatedEncryptionWithAssociatedData, CertificateCompressionAlgorithm,
7 KeyDerivationFunction,
8};
9use crate::tls::{
10 ApplicationProtocol, CipherSuite, ECPointFormat, ExtensionId, ProtocolVersion, SignatureScheme,
11 SupportedGroup, enums::CompressionAlgorithm,
12};
13
14#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
15/// When a client first connects to a server, it is required to send
16/// the ClientHello as its first message.
17///
18/// The ClientHello contains random data, cipher suites,
19/// legacy content from <= TLS.12 and extensions.
20///
21/// For Rama however we only focus on the parts which
22/// a user might want to inspect and/or set.
23pub struct ClientHello {
24 pub(super) protocol_version: ProtocolVersion,
25 pub(super) cipher_suites: Vec<CipherSuite>,
26 pub(super) compression_algorithms: Vec<CompressionAlgorithm>,
27 pub(super) extensions: Vec<ClientHelloExtension>,
28}
29
30impl ClientHello {
31 pub fn new(
32 protocol_version: ProtocolVersion,
33 cipher_suites: Vec<CipherSuite>,
34 compression_algorithms: Vec<CompressionAlgorithm>,
35 extensions: Vec<ClientHelloExtension>,
36 ) -> Self {
37 Self {
38 protocol_version,
39 cipher_suites,
40 compression_algorithms,
41 extensions,
42 }
43 }
44
45 /// Return all [`ProtocolVersion`]s defined in this [`ClientHello`].
46 pub fn protocol_version(&self) -> ProtocolVersion {
47 self.protocol_version
48 }
49
50 /// Return all [`CipherSuite`]s defined in this [`ClientHello`].
51 pub fn cipher_suites(&self) -> &[CipherSuite] {
52 &self.cipher_suites[..]
53 }
54
55 /// Return all [`CompressionAlgorithm`]s defined in this [`ClientHello`].
56 pub fn compression_algorithms(&self) -> &[CompressionAlgorithm] {
57 &self.compression_algorithms[..]
58 }
59
60 /// Return all [`ClientHelloExtension`]s defined in this [`ClientHello`].
61 pub fn extensions(&self) -> &[ClientHelloExtension] {
62 &self.extensions[..]
63 }
64
65 /// Return the server name (SNI) if it is set in the [`ClientHelloExtension`] defined in this [`ClientHello`].
66 ///
67 /// See [`ClientHelloExtension::ServerName`] for more information about the server name.
68 pub fn ext_server_name(&self) -> Option<&Host> {
69 for ext in &self.extensions {
70 if let ClientHelloExtension::ServerName(host) = ext {
71 return host.as_ref();
72 }
73 }
74 None
75 }
76
77 /// Return the elliptic curves supported by this client
78 /// if it is set in the [`ClientHelloExtension`] defined in this [`ClientHello`].
79 ///
80 /// See [`ClientHelloExtension::SupportedGroups`] for more information about these curves.
81 pub fn ext_supported_groups(&self) -> Option<&[SupportedGroup]> {
82 for ext in &self.extensions {
83 if let ClientHelloExtension::SupportedGroups(groups) = ext {
84 return Some(&groups[..]);
85 }
86 }
87 None
88 }
89
90 /// Return the EC point formats supported by this client
91 /// if it is set in the [`ClientHelloExtension`] defined in this [`ClientHello`].
92 ///
93 /// See [`ClientHelloExtension::ECPointFormats`] for more information about this.
94 pub fn ext_ec_point_formats(&self) -> Option<&[ECPointFormat]> {
95 for ext in &self.extensions {
96 if let ClientHelloExtension::ECPointFormats(formats) = ext {
97 return Some(&formats[..]);
98 }
99 }
100 None
101 }
102
103 /// Return the signature algorithms supported by this client
104 /// if it is set in the [`ClientHelloExtension`] defined in this [`ClientHello`].
105 ///
106 /// See [`ClientHelloExtension::SignatureAlgorithms`] for more information about these algorithms
107 pub fn ext_signature_algorithms(&self) -> Option<&[SignatureScheme]> {
108 for ext in &self.extensions {
109 if let ClientHelloExtension::SignatureAlgorithms(algos) = ext {
110 return Some(&algos[..]);
111 }
112 }
113 None
114 }
115
116 /// Return the application layer protocols supported for negotiation by this client
117 /// if it is set in the [`ClientHelloExtension`] defined in this [`ClientHello`].
118 ///
119 /// See [`ClientHelloExtension::ApplicationLayerProtocolNegotiation`] for more information about these protocols (ALPN).
120 pub fn ext_alpn(&self) -> Option<&[ApplicationProtocol]> {
121 for ext in &self.extensions {
122 if let ClientHelloExtension::ApplicationLayerProtocolNegotiation(alpns) = ext {
123 return Some(&alpns[..]);
124 }
125 }
126 None
127 }
128
129 /// Return the TLS versions supported by this client
130 /// if it is set in the [`ClientHelloExtension`] defined in this [`ClientHello`].
131 ///
132 /// See [`ClientHelloExtension::SupportedVersions`] for more information about these versions
133 pub fn supported_versions(&self) -> Option<&[ProtocolVersion]> {
134 for ext in &self.extensions {
135 if let ClientHelloExtension::SupportedVersions(versions) = ext {
136 return Some(&versions[..]);
137 }
138 }
139 None
140 }
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
144/// Extensions that can be set in a [`ClientHello`] message by a TLS client.
145///
146/// While its name may infer that an extension is by definition optional,
147/// you would be wrong to think so. These extensions are also used
148/// to fill historical gaps in the TLS specifications and as a consequence
149/// there are a couple of extensions that are pretty much in any [`ClientHello`] message.
150///
151/// Most of the defined variants of this _enum_ are examples of such "required" extensions.
152/// Extensions like [`ClientHelloExtension::ApplicationLayerProtocolNegotiation`]
153/// are not required but due to benefits it offers it also is pretty much always present,
154/// as it helps save application negotiation roundtrips;
155pub enum ClientHelloExtension {
156 /// name of the server the client intends to connect to
157 ///
158 /// TLS does not provide a mechanism for a client to tell a server the
159 /// name of the server it is contacting. It may be desirable for clients
160 /// to provide this information to facilitate secure connections to
161 /// servers that host multiple 'virtual' servers at a single underlying
162 /// network address.
163 ///
164 /// In order to provide any of the server names, clients MAY include an
165 /// extension of type "server_name" in the (extended) client hello.
166 ///
167 /// # Reference
168 ///
169 /// - <https://www.iana.org/go/rfc6066>
170 /// - <https://www.iana.org/go/rfc9261>
171 ServerName(Option<Host>),
172 /// indicates which elliptic curves the client supports
173 ///
174 /// This extension is required... despite being an extension.
175 ///
176 /// Renamed from EllipticCurves, which some material might still reference it as.
177 ///
178 /// # Reference
179 ///
180 /// - <https://www.iana.org/go/rfc8422>
181 /// - <https://www.iana.org/go/rfc7919>
182 SupportedGroups(Vec<SupportedGroup>),
183 /// indicates the set of point formats that the client can parse
184 ///
185 /// For this extension, the opaque extension_data field contains ECPointFormatList.
186 ///
187 /// # Reference
188 ///
189 /// - <https://www.iana.org/go/rfc8422>
190 ECPointFormats(Vec<ECPointFormat>),
191 /// Algorithms supported by the client for signing certificates.
192 ///
193 /// # Reference
194 ///
195 /// - <https://www.iana.org/go/rfc8446>
196 SignatureAlgorithms(Vec<SignatureScheme>),
197 /// Application Layer Protocol Negotiation, often referred to as ALPN.
198 ///
199 /// Used to indicate the application layer protocols the client supports,
200 /// e.g. h2 or h3. Allowing the server to immediately serve content
201 /// using one of the supported protocols avoiding otherwise
202 /// wasteful upgrade roundtrips.
203 ///
204 /// # Reference
205 ///
206 /// - <https://www.iana.org/go/rfc7301>
207 ApplicationLayerProtocolNegotiation(Vec<ApplicationProtocol>),
208 /// used by the client to indicate which versions of TLS it supports
209 ///
210 /// # Reference
211 ///
212 /// - <https://www.iana.org/go/rfc8446>
213 SupportedVersions(Vec<ProtocolVersion>),
214 /// The algorithm used to compress the certificate.
215 ///
216 /// # Reference
217 ///
218 /// - <https://datatracker.ietf.org/doc/html/rfc8879>
219 CertificateCompression(Vec<CertificateCompressionAlgorithm>),
220 /// The maximum size of a record.
221 ///
222 /// # Reference
223 ///
224 /// - <https://datatracker.ietf.org/doc/html/rfc8449>
225 RecordSizeLimit(u16),
226 /// Delegated credentials
227 ///
228 /// # Reference
229 ///
230 /// - <https://datatracker.ietf.org/doc/html/rfc9345>
231 DelegatedCredentials(Vec<SignatureScheme>),
232 /// Encrypted hello send by the client
233 /// # Reference
234 ///
235 /// - <https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni/>
236 EncryptedClientHello(ECHClientHello),
237 /// Any extension not supported by Rama,
238 /// as it is still to be done or considered out of scope.
239 Opaque {
240 /// extension id
241 id: ExtensionId,
242 /// extension data
243 data: Vec<u8>,
244 },
245}
246
247impl ClientHelloExtension {
248 /// returns the [`ExtensionId`] which identifies this [`ClientHelloExtension`].
249 pub fn id(&self) -> ExtensionId {
250 match self {
251 ClientHelloExtension::ServerName(_) => ExtensionId::SERVER_NAME,
252 ClientHelloExtension::SupportedGroups(_) => ExtensionId::SUPPORTED_GROUPS,
253 ClientHelloExtension::ECPointFormats(_) => ExtensionId::EC_POINT_FORMATS,
254 ClientHelloExtension::SignatureAlgorithms(_) => ExtensionId::SIGNATURE_ALGORITHMS,
255 ClientHelloExtension::ApplicationLayerProtocolNegotiation(_) => {
256 ExtensionId::APPLICATION_LAYER_PROTOCOL_NEGOTIATION
257 }
258 ClientHelloExtension::SupportedVersions(_) => ExtensionId::SUPPORTED_VERSIONS,
259 ClientHelloExtension::CertificateCompression(_) => ExtensionId::COMPRESS_CERTIFICATE,
260 ClientHelloExtension::DelegatedCredentials(_) => ExtensionId::DELEGATED_CREDENTIAL,
261 ClientHelloExtension::RecordSizeLimit(_) => ExtensionId::RECORD_SIZE_LIMIT,
262 ClientHelloExtension::EncryptedClientHello(_) => ExtensionId::ENCRYPTED_CLIENT_HELLO,
263 ClientHelloExtension::Opaque { id, .. } => *id,
264 }
265 }
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
269/// Client Hello contents send by ech
270pub enum ECHClientHello {
271 /// Send when message is in the outer (unencrypted) part of client hello. It contains
272 /// encryption data and the encrypted client hello.
273 Outer(ECHClientHelloOuter),
274 /// The inner extension has an empty payload, which is included because TLS servers are
275 /// not allowed to provide extensions in ServerHello which were not included in ClientHello.
276 /// And when using encrypted client hello the server will discard the outer unencrypted one,
277 /// and only look at the encrypted client hello. So we have to add this extension again there so
278 /// the server knows ECH is supported by the client.
279 Inner,
280}
281
282#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
283/// Data send by ech hello message when it is in the outer part
284pub struct ECHClientHelloOuter {
285 pub cipher_suite: HpkeSymmetricCipherSuite,
286 pub config_id: u8,
287 pub enc: Vec<u8>,
288 pub payload: Vec<u8>,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
292/// HPKE KDF and AEAD pair used to encrypt ClientHello
293pub struct HpkeSymmetricCipherSuite {
294 pub kdf_id: KeyDerivationFunction,
295 pub aead_id: AuthenticatedEncryptionWithAssociatedData,
296}
297
298impl Display for HpkeSymmetricCipherSuite {
299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
300 write!(f, "{},{}", self.kdf_id, self.aead_id)
301 }
302}