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