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