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}