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