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