Skip to main content

rustls/
common_state.rs

1use alloc::boxed::Box;
2use alloc::vec::Vec;
3use core::fmt;
4use core::ops::{Deref, DerefMut, Range};
5
6use pki_types::{DnsName, FipsStatus};
7
8use crate::client::EchStatus;
9use crate::conn::{DataKind, Exporter, KeyingMaterialExporter, ReceivePath, SendOutput, SendPath};
10use crate::crypto::cipher::{EncodableVersion, Payload};
11use crate::crypto::kx::SupportedKxGroup;
12use crate::enums::{ApplicationProtocol, ProtocolVersion};
13use crate::error::{AlertDescription, ApiMisuse, Error};
14use crate::hash_hs::HandshakeHash;
15use crate::msgs::{
16    AlertLevel, Codec, Delocator, HandshakeMessagePayload, Locator, Message, MessagePayload,
17};
18use crate::quic::{self, QuicOutput};
19use crate::suites::SupportedCipherSuite;
20use crate::verify::VerifiedIdentity;
21
22/// Connection state common to both client and server connections.
23pub struct CommonState {
24    pub(crate) outputs: ConnectionOutputs,
25    pub(crate) send: SendPath,
26    pub(crate) recv: ReceivePath,
27}
28
29impl CommonState {
30    pub(crate) fn new(side: Side, fips: FipsStatus) -> Self {
31        Self {
32            outputs: ConnectionOutputs::new(fips),
33            send: SendPath::default(),
34            recv: ReceivePath::new(side),
35        }
36    }
37
38    /// Writes a `close_notify` warning alert to into the `tls` buffer.
39    ///
40    /// This informs the peer that the connection is being closed. Does nothing if any
41    /// `close_notify` or fatal alert was already sent.
42    pub fn send_close_notify(&mut self, tls: &mut Vec<u8>) -> Result<(), Error> {
43        self.send.send_close_notify(tls)
44    }
45
46    /// Returns true if the connection is currently performing the TLS handshake.
47    ///
48    /// During this time plaintext written to the connection is buffered in memory. After
49    /// [`Connection::read_tls()`] has been called, this might start to return `false`
50    /// while the final handshake packets still need to be extracted from the connection's buffers.
51    ///
52    /// [`Connection::read_tls()`]: crate::Connection::read_tls
53    pub fn is_handshaking(&self) -> bool {
54        !(self.send.may_send_application_data && self.recv.may_receive_application_data)
55    }
56}
57
58impl Deref for CommonState {
59    type Target = ConnectionOutputs;
60
61    fn deref(&self) -> &Self::Target {
62        &self.outputs
63    }
64}
65
66impl DerefMut for CommonState {
67    fn deref_mut(&mut self) -> &mut Self::Target {
68        &mut self.outputs
69    }
70}
71
72impl fmt::Debug for CommonState {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        f.debug_struct("CommonState")
75            .finish_non_exhaustive()
76    }
77}
78
79/// Facts about the connection learned through the handshake.
80pub struct ConnectionOutputs {
81    pub(crate) fips: FipsStatus,
82    negotiated_version: Option<ProtocolVersion>,
83    handshake_kind: Option<HandshakeKind>,
84    suite: Option<SupportedCipherSuite>,
85    negotiated_kx_group: Option<&'static dyn SupportedKxGroup>,
86    alpn_protocol: Option<ApplicationProtocol<'static>>,
87    peer_identity: Option<VerifiedIdentity<'static>>,
88    extended_main_secret: Option<bool>,
89    exporter: Option<Box<dyn Exporter>>,
90    pub(crate) early_exporter: Option<Box<dyn Exporter>>,
91}
92
93impl ConnectionOutputs {
94    fn new(fips: FipsStatus) -> Self {
95        Self {
96            fips,
97            negotiated_version: None,
98            handshake_kind: None,
99            suite: None,
100            negotiated_kx_group: None,
101            alpn_protocol: None,
102            peer_identity: None,
103            extended_main_secret: None,
104            exporter: None,
105            early_exporter: None,
106        }
107    }
108
109    /// Retrieves the certificate chain or the raw public key used by the peer to authenticate.
110    ///
111    /// This is made available for both full and resumed handshakes.
112    ///
113    /// For clients, this is the identity of the server. For servers, this is the identity of the
114    /// client, if client authentication was completed.
115    ///
116    /// The return value is None until this value is available.
117    pub fn peer_identity(&self) -> Option<&VerifiedIdentity<'static>> {
118        self.peer_identity.as_ref()
119    }
120
121    /// Retrieves the protocol agreed with the peer via ALPN.
122    ///
123    /// A return value of `None` after handshake completion
124    /// means no protocol was agreed (because no protocols
125    /// were offered or accepted by the peer).
126    pub fn alpn_protocol(&self) -> Option<&ApplicationProtocol<'static>> {
127        self.alpn_protocol.as_ref()
128    }
129
130    /// Retrieves the cipher suite agreed with the peer.
131    ///
132    /// This returns None until the cipher suite is agreed.
133    pub fn negotiated_cipher_suite(&self) -> Option<SupportedCipherSuite> {
134        self.suite
135    }
136
137    /// Retrieves the key exchange group agreed with the peer.
138    ///
139    /// This function may return `None` depending on the state of the connection,
140    /// the type of handshake, and the protocol version.
141    ///
142    /// If [`CommonState::is_handshaking()`] is true this function will return `None`.
143    /// Similarly, if the [`ConnectionOutputs::handshake_kind()`] is [`HandshakeKind::Resumed`]
144    /// and the [`ConnectionOutputs::protocol_version()`] is TLS 1.2, then no key exchange will have
145    /// occurred and this function will return `None`.
146    pub fn negotiated_key_exchange_group(&self) -> Option<&'static dyn SupportedKxGroup> {
147        self.negotiated_kx_group
148    }
149
150    /// Retrieves the protocol version agreed with the peer.
151    ///
152    /// This returns `None` until the version is agreed.
153    pub fn protocol_version(&self) -> Option<ProtocolVersion> {
154        self.negotiated_version
155    }
156
157    /// Return the FIPS validation status of the connection.
158    ///
159    /// This is different from [`CryptoProvider::fips()`][]:
160    /// it is concerned only with cryptography, whereas this _also_ covers TLS-level
161    /// configuration that NIST recommends, as well as ECH HPKE suites if applicable.
162    ///
163    /// [`CryptoProvider::fips()`]: crate::crypto::CryptoProvider::fips()
164    pub fn fips(&self) -> FipsStatus {
165        self.fips
166    }
167
168    /// Whether the Extended Main Secret extension was negotiated.
169    ///
170    /// Returns:
171    /// - `None` until the handshake reaches the point where this is known.
172    /// - `None` for TLS 1.3, where the extension does not apply.
173    /// - `Some(true)` for TLS 1.2 if the extension was negotiated.
174    /// - `Some(false)` otherwise.
175    pub fn extended_main_secret(&self) -> Option<bool> {
176        self.extended_main_secret
177    }
178
179    /// Which kind of handshake was performed.
180    ///
181    /// This tells you whether the handshake was a resumption or not.
182    ///
183    /// This will return `None` before it is known which sort of
184    /// handshake occurred.
185    pub fn handshake_kind(&self) -> Option<HandshakeKind> {
186        self.handshake_kind
187    }
188
189    #[doc = include_str!("doc/early_exporter.md")]
190    pub fn early_exporter(&mut self) -> Result<KeyingMaterialExporter, Error> {
191        match self.early_exporter.take() {
192            Some(inner) => Ok(KeyingMaterialExporter { inner }),
193            None => Err(ApiMisuse::ExporterNotAvailable.into()),
194        }
195    }
196
197    #[doc = include_str!("doc/exporter.md")]
198    pub fn exporter(&mut self) -> Result<KeyingMaterialExporter, Error> {
199        match self.exporter.take() {
200            Some(inner) => Ok(KeyingMaterialExporter { inner }),
201            None => Err(ApiMisuse::ExporterNotAvailable.into()),
202        }
203    }
204
205    pub(super) fn into_kernel_parts(self) -> Option<(ProtocolVersion, SupportedCipherSuite)> {
206        let Self {
207            negotiated_version,
208            suite,
209            ..
210        } = self;
211
212        match (negotiated_version, suite) {
213            (Some(version), Some(suite)) => Some((version, suite)),
214            _ => None,
215        }
216    }
217}
218
219impl ConnectionOutput for ConnectionOutputs {
220    fn handle(&mut self, ev: OutputEvent<'_>) {
221        match ev {
222            OutputEvent::ApplicationProtocol(protocol) => {
223                self.alpn_protocol = Some(ApplicationProtocol::from(protocol.as_ref()).to_owned())
224            }
225            OutputEvent::CipherSuite(suite) => self.suite = Some(suite),
226            OutputEvent::EarlyExporter(exporter) => self.early_exporter = Some(exporter),
227            OutputEvent::Exporter(exporter) => self.exporter = Some(exporter),
228            OutputEvent::ExtendedMainSecret(ems) => self.extended_main_secret = Some(ems),
229            OutputEvent::HandshakeKind(hk) => {
230                assert!(self.handshake_kind.is_none());
231                self.handshake_kind = Some(hk);
232            }
233            OutputEvent::KeyExchangeGroup(kxg) => {
234                assert!(self.negotiated_kx_group.is_none());
235                self.negotiated_kx_group = Some(kxg);
236            }
237            OutputEvent::PeerIdentity(identity) => self.peer_identity = Some(identity),
238            OutputEvent::ProtocolVersion(ver) => {
239                self.negotiated_version = Some(ver);
240            }
241        }
242    }
243}
244
245impl fmt::Debug for ConnectionOutputs {
246    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247        let Self {
248            fips,
249            negotiated_version,
250            handshake_kind,
251            suite,
252            negotiated_kx_group,
253            alpn_protocol,
254            peer_identity,
255            extended_main_secret,
256            exporter: _,
257            early_exporter: _,
258        } = self;
259        f.debug_struct("ConnectionOutputs")
260            .field("fips", fips)
261            .field("negotiated_version", negotiated_version)
262            .field("handshake_kind", handshake_kind)
263            .field("suite", suite)
264            .field("negotiated_kx_group", negotiated_kx_group)
265            .field("alpn_protocol", alpn_protocol)
266            .field("peer_identity", peer_identity)
267            .field("extended_main_secret", extended_main_secret)
268            .finish_non_exhaustive()
269    }
270}
271
272/// Send an alert via `output` if `error` specifies one.
273pub(crate) fn maybe_send_fatal_alert(
274    send: &mut dyn SendOutput,
275    error: &Error,
276    tls: &mut Vec<u8>,
277) -> Result<(), Error> {
278    let Ok(alert) = AlertDescription::try_from(error) else {
279        return Ok(());
280    };
281    send.send_alert(AlertLevel::Fatal, alert, tls)
282}
283
284/// Describes which sort of handshake happened.
285#[derive(Debug, PartialEq, Clone, Copy)]
286#[non_exhaustive]
287pub enum HandshakeKind {
288    /// A full handshake.
289    ///
290    /// This is the typical TLS connection initiation process when resumption is
291    /// not available, and the initial `ClientHello` was accepted by the server.
292    Full,
293
294    /// A full TLS1.3 handshake, with an extra round-trip for a `HelloRetryRequest`.
295    ///
296    /// The server can respond with a `HelloRetryRequest` if the initial `ClientHello`
297    /// is unacceptable for several reasons, the most likely being if no supported key
298    /// shares were offered by the client.
299    FullWithHelloRetryRequest,
300
301    /// A resumed handshake.
302    ///
303    /// Resumed handshakes involve fewer round trips and less cryptography than
304    /// full ones, but can only happen when the peers have previously done a full
305    /// handshake together, and then remember data about it.
306    Resumed,
307
308    /// A resumed handshake, with an extra round-trip for a `HelloRetryRequest`.
309    ///
310    /// The server can respond with a `HelloRetryRequest` if the initial `ClientHello`
311    /// is unacceptable for several reasons, but this does not prevent the client
312    /// from resuming.
313    ResumedWithHelloRetryRequest,
314}
315
316/// The route for handshake state machine to surface determinations about the connection.
317pub(crate) trait Output<'m> {
318    fn emit(&mut self, ev: Event);
319
320    fn output(&mut self, ev: OutputEvent<'_>);
321
322    fn send_msg(&mut self, m: Message<'_>, must_encrypt: bool) -> Result<(), Error>;
323
324    fn quic(&mut self) -> Option<&mut dyn QuicOutput> {
325        None
326    }
327
328    fn received_plaintext(&mut self, _payload: DataKind<Payload<'m>>) {}
329
330    fn start_traffic(&mut self);
331
332    fn receive(&mut self) -> &mut ReceivePath;
333
334    fn send(&mut self) -> &mut dyn SendOutput;
335}
336
337pub(crate) trait ConnectionOutput {
338    fn handle(&mut self, ev: OutputEvent<'_>);
339}
340
341/// The set of events output by the low-level handshake state machine.
342pub(crate) enum Event {
343    EarlyData(EarlyDataEvent),
344    EchStatus(EchStatus),
345    ReceivedServerName(Option<DnsName<'static>>),
346    ResumptionData(Vec<u8>),
347}
348
349pub(crate) enum OutputEvent<'a> {
350    ApplicationProtocol(ApplicationProtocol<'a>),
351    CipherSuite(SupportedCipherSuite),
352    EarlyExporter(Box<dyn Exporter>),
353    Exporter(Box<dyn Exporter>),
354    ExtendedMainSecret(bool),
355    HandshakeKind(HandshakeKind),
356    KeyExchangeGroup(&'static dyn SupportedKxGroup),
357    PeerIdentity(VerifiedIdentity<'static>),
358    ProtocolVersion(ProtocolVersion),
359}
360
361pub(crate) enum EarlyDataEvent {
362    /// client: declares the maximum amount of early data that can be sent
363    Enable(usize),
364    /// client: early data can now be sent using the record layer as normal
365    Start,
366    /// client: early data phase has closed after sending EndOfEarlyData
367    Finished,
368    /// client: the server accepted an early_data offer
369    Accepted,
370    /// client: the server rejected our request for early data
371    Rejected,
372}
373
374/// Lifetime-erased equivalent to [`Payload`]
375///
376/// Stores an index into [`Payload`] buffer enabling in-place decryption
377/// without holding a lifetime to the receive buffer.
378pub(crate) enum UnborrowedPayload {
379    Unborrowed(Range<usize>),
380    Owned(Vec<u8>),
381}
382
383impl UnborrowedPayload {
384    /// Convert [`Payload`] into [`UnborrowedPayload`] which stores a range
385    /// into the [`Payload`] slice without borrowing such that it can be later
386    /// reborrowed.
387    ///
388    /// # Panics
389    ///
390    /// Passed [`Locator`] must have been created from the same slice which
391    /// contains the payload.
392    pub(crate) fn unborrow(locator: &Locator, payload: Payload<'_>) -> Self {
393        match payload {
394            Payload::Borrowed(payload) => Self::Unborrowed(locator.locate(payload)),
395            Payload::Owned(payload) => Self::Owned(payload),
396        }
397    }
398
399    /// Convert [`UnborrowedPayload`] back into [`Payload`]
400    ///
401    /// # Panics
402    ///
403    /// Passed [`Delocator`] must have been created from the same slice that
404    /// [`UnborrowedPayload`] was originally unborrowed from.
405    pub(crate) fn reborrow<'b>(self, delocator: &Delocator<'b>) -> Payload<'b> {
406        match self {
407            Self::Unborrowed(range) => Payload::Borrowed(delocator.slice_from_range(&range)),
408            Self::Owned(payload) => Payload::Owned(payload),
409        }
410    }
411}
412
413/// Side of the connection.
414#[expect(clippy::exhaustive_enums)]
415#[derive(Clone, Copy, Debug, PartialEq)]
416pub enum Side {
417    /// A client initiates the connection.
418    Client,
419    /// A server waits for a client to connect.
420    Server,
421}
422
423/// Transport protocol in use for a connection.
424#[derive(Copy, Clone, Eq, PartialEq, Debug)]
425#[non_exhaustive]
426pub enum Protocol {
427    /// TCP-TLS, standardized in RFC 5246 and RFC 9846
428    Tcp,
429    /// QUIC, standardized in RFC 9001
430    Quic(quic::Version),
431}
432
433impl Protocol {
434    pub(crate) fn is_quic(&self) -> bool {
435        matches!(self, Self::Quic(_))
436    }
437
438    pub(crate) fn supports_version(&self, version: ProtocolVersion) -> bool {
439        match self {
440            Self::Quic(_) => version == ProtocolVersion::TLSv1_3,
441            Self::Tcp => true,
442        }
443    }
444}
445
446pub(crate) struct HandshakeFlight<'a, const TLS13: bool> {
447    pub(crate) transcript: &'a mut HandshakeHash,
448    body: Vec<u8>,
449}
450
451impl<'a, const TLS13: bool> HandshakeFlight<'a, TLS13> {
452    pub(crate) fn new(transcript: &'a mut HandshakeHash) -> Self {
453        Self {
454            transcript,
455            body: Vec::with_capacity(512),
456        }
457    }
458
459    pub(crate) fn add(&mut self, hs: HandshakeMessagePayload<'_>) {
460        let start_len = self.body.len();
461        hs.encode(&mut self.body);
462        self.transcript
463            .add(&self.body[start_len..]);
464    }
465
466    pub(crate) fn finish(self, output: &mut dyn Output<'_>) -> Result<(), Error> {
467        let m = Message {
468            version: EncodableVersion::Legacy(match TLS13 {
469                true => ProtocolVersion::TLSv1_3,
470                false => ProtocolVersion::TLSv1_2,
471            }),
472            payload: MessagePayload::HandshakeFlight(Payload::new(self.body)),
473        };
474
475        output.send_msg(m, TLS13)
476    }
477}
478
479pub(crate) type HandshakeFlightTls12<'a> = HandshakeFlight<'a, false>;
480pub(crate) type HandshakeFlightTls13<'a> = HandshakeFlight<'a, true>;