Coverage Report

Created: 2026-06-28 08:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rustls-0.23.40/src/conn/kernel.rs
Line
Count
Source
1
//! Kernel connection API.
2
//!
3
//! This module gives you the bare minimum you need to implement a TLS connection
4
//! that does its own encryption and decryption while still using rustls to manage
5
//! connection secrets and session tickets. It is intended for use cases like kTLS
6
//! where you want to use rustls to establish the connection but want to use
7
//! something else to do the encryption/decryption after that.
8
//!
9
//! There are only two things that [`KernelConnection`] is able to do:
10
//! 1. Compute new traffic secrets when a key update occurs.
11
//! 2. Save received session tickets sent by a server peer.
12
//!
13
//! That's it. Everything else you will need to implement yourself.
14
//!
15
//! # Entry Point
16
//! The entry points into this API are
17
//! [`UnbufferedClientConnection::dangerous_into_kernel_connection`][client-into]
18
//! and
19
//! [`UnbufferedServerConnection::dangerous_into_kernel_connection`][server-into].
20
//!
21
//! In order to actually create an [`KernelConnection`] all of the following
22
//! must be true:
23
//! - the connection must have completed its handshake,
24
//! - the connection must have no buffered TLS data waiting to be sent, and,
25
//! - the config used to create the connection must have `enable_extract_secrets`
26
//!   set to true.
27
//!
28
//! This sounds fairly complicated to achieve at first glance. However, if you
29
//! drive an unbuffered connection through the handshake until it returns
30
//! [`WriteTraffic`] then it will end up in an appropriate state to convert
31
//! into an external connection.
32
//!
33
//! [client-into]: crate::client::UnbufferedClientConnection::dangerous_into_kernel_connection
34
//! [server-into]: crate::server::UnbufferedServerConnection::dangerous_into_kernel_connection
35
//! [`WriteTraffic`]: crate::unbuffered::ConnectionState::WriteTraffic
36
//!
37
//! # Cipher Suite Confidentiality Limits
38
//! Some cipher suites (notably AES-GCM) have vulnerabilities where they are no
39
//! longer secure once a certain number of messages have been sent. Normally,
40
//! rustls tracks how many messages have been written or read and will
41
//! automatically either refresh keys or emit an error when approaching the
42
//! confidentiality limit of the cipher suite.
43
//!
44
//! [`KernelConnection`] has no way to track this. It is the responsibility
45
//! of the user of the API to track approximately how many messages have been
46
//! sent and either refresh the traffic keys or abort the connection before the
47
//! confidentiality limit is reached.
48
//!
49
//! You can find the current confidentiality limit by looking at
50
//! [`CipherSuiteCommon::confidentiality_limit`] for the cipher suite selected
51
//! by the connection.
52
//!
53
//! [`CipherSuiteCommon::confidentiality_limit`]: crate::CipherSuiteCommon::confidentiality_limit
54
//! [`KernelConnection`]: crate::kernel::KernelConnection
55
56
use alloc::boxed::Box;
57
use core::marker::PhantomData;
58
59
use crate::client::ClientConnectionData;
60
use crate::common_state::Protocol;
61
use crate::msgs::codec::Codec;
62
use crate::msgs::handshake::{CertificateChain, NewSessionTicketPayloadTls13};
63
use crate::quic::Quic;
64
use crate::{CommonState, ConnectionTrafficSecrets, Error, ProtocolVersion, SupportedCipherSuite};
65
66
/// A kernel connection.
67
///
68
/// This does not directly wrap a kernel connection, rather it gives you the
69
/// minimal interfaces you need to implement a well-behaved TLS connection on
70
/// top of kTLS.
71
///
72
/// See the [`crate::kernel`] module docs for more details.
73
pub struct KernelConnection<Data> {
74
    state: Box<dyn KernelState>,
75
76
    peer_certificates: Option<CertificateChain<'static>>,
77
    quic: Quic,
78
79
    negotiated_version: ProtocolVersion,
80
    protocol: Protocol,
81
    suite: SupportedCipherSuite,
82
83
    _data: PhantomData<Data>,
84
}
85
86
impl<Data> KernelConnection<Data> {
87
0
    pub(crate) fn new(state: Box<dyn KernelState>, common: CommonState) -> Result<Self, Error> {
88
        Ok(Self {
89
0
            state,
90
91
0
            peer_certificates: common.peer_certificates,
92
0
            quic: common.quic,
93
0
            negotiated_version: common
94
0
                .negotiated_version
95
0
                .ok_or(Error::HandshakeNotComplete)?,
96
0
            protocol: common.protocol,
97
0
            suite: common
98
0
                .suite
99
0
                .ok_or(Error::HandshakeNotComplete)?,
100
101
0
            _data: PhantomData,
102
        })
103
0
    }
Unexecuted instantiation: <rustls::conn::kernel::KernelConnection<rustls::client::client_conn::ClientConnectionData>>::new
Unexecuted instantiation: <rustls::conn::kernel::KernelConnection<rustls::server::server_conn::ServerConnectionData>>::new
104
105
    /// Retrieves the ciphersuite agreed with the peer.
106
0
    pub fn negotiated_cipher_suite(&self) -> SupportedCipherSuite {
107
0
        self.suite
108
0
    }
109
110
    /// Retrieves the protocol version agreed with the peer.
111
0
    pub fn protocol_version(&self) -> ProtocolVersion {
112
0
        self.negotiated_version
113
0
    }
114
115
    /// Update the traffic secret used for encrypting messages sent to the peer.
116
    ///
117
    /// Returns the new traffic secret and initial sequence number to use.
118
    ///
119
    /// In order to use the new secret you should send a TLS 1.3 key update to
120
    /// the peer and then use the new traffic secrets to encrypt any future
121
    /// messages.
122
    ///
123
    /// Note that it is only possible to update the traffic secrets on a TLS 1.3
124
    /// connection. Attempting to do so on a non-TLS 1.3 connection will result
125
    /// in an error.
126
0
    pub fn update_tx_secret(&mut self) -> Result<(u64, ConnectionTrafficSecrets), Error> {
127
        // The sequence number always starts at 0 after a key update.
128
0
        self.state
129
0
            .update_secrets(Direction::Transmit)
130
0
            .map(|secret| (0, secret))
131
0
    }
132
133
    /// Update the traffic secret used for decrypting messages received from the
134
    /// peer.
135
    ///
136
    /// Returns the new traffic secret and initial sequence number to use.
137
    ///
138
    /// You should call this method once you receive a TLS 1.3 key update message
139
    /// from the peer.
140
    ///
141
    /// Note that it is only possible to update the traffic secrets on a TLS 1.3
142
    /// connection. Attempting to do so on a non-TLS 1.3 connection will result
143
    /// in an error.
144
0
    pub fn update_rx_secret(&mut self) -> Result<(u64, ConnectionTrafficSecrets), Error> {
145
        // The sequence number always starts at 0 after a key update.
146
0
        self.state
147
0
            .update_secrets(Direction::Receive)
148
0
            .map(|secret| (0, secret))
149
0
    }
150
}
151
152
impl KernelConnection<ClientConnectionData> {
153
    /// Handle a `new_session_ticket` message from the peer.
154
    ///
155
    /// This will register the session ticket within with rustls so that it can
156
    /// be used to establish future TLS connections.
157
    ///
158
    /// # Getting the right payload
159
    ///
160
    /// This method expects to be passed the inner payload of the handshake
161
    /// message. This means that you will need to parse the header of the
162
    /// handshake message in order to determine the correct payload to pass in.
163
    /// The message format is described in [RFC 8446 section 4][0]. `payload`
164
    /// should not include the `msg_type` or `length` fields.
165
    ///
166
    /// Code to parse out the payload should look something like this
167
    /// ```no_run
168
    /// use rustls::{ContentType, HandshakeType};
169
    /// use rustls::kernel::KernelConnection;
170
    /// use rustls::client::ClientConnectionData;
171
    ///
172
    /// # fn doctest(conn: &mut KernelConnection<ClientConnectionData>, typ: ContentType, message: &[u8]) -> Result<(), rustls::Error> {
173
    /// let conn: &mut KernelConnection<ClientConnectionData> = // ...
174
    /// #   conn;
175
    /// let typ: ContentType = // ...
176
    /// #   typ;
177
    /// let mut message: &[u8] = // ...
178
    /// #   message;
179
    ///
180
    /// // Processing for other messages not included in this example
181
    /// assert_eq!(typ, ContentType::Handshake);
182
    ///
183
    /// // There may be multiple handshake payloads within a single handshake message.
184
    /// while !message.is_empty() {
185
    ///     let (typ, len, rest) = match message {
186
    ///         &[typ, a, b, c, ref rest @ ..] => (
187
    ///             HandshakeType::from(typ),
188
    ///             u32::from_be_bytes([0, a, b, c]) as usize,
189
    ///             rest
190
    ///         ),
191
    ///         _ => panic!("error handling not included in this example")
192
    ///     };
193
    ///
194
    ///     // Processing for other messages not included in this example.
195
    ///     assert_eq!(typ, HandshakeType::NewSessionTicket);
196
    ///     assert!(rest.len() >= len, "invalid handshake message");
197
    ///
198
    ///     let (payload, rest) = rest.split_at(len);
199
    ///     message = rest;
200
    ///
201
    ///     conn.handle_new_session_ticket(payload)?;
202
    /// }
203
    /// # Ok(())
204
    /// # }
205
    /// ```
206
    ///
207
    /// # Errors
208
    /// This method will return an error if:
209
    /// - This connection is not a TLS 1.3 connection (in TLS 1.2 session tickets
210
    ///   are sent as part of the handshake).
211
    /// - The provided payload is not a valid `new_session_ticket` payload or has
212
    ///   extra unparsed trailing data.
213
    /// - An error occurs while the connection updates the session ticket store.
214
    ///
215
    /// [0]: https://datatracker.ietf.org/doc/html/rfc8446#section-4
216
0
    pub fn handle_new_session_ticket(&mut self, payload: &[u8]) -> Result<(), Error> {
217
        // We want to return a more specific error here first if this is called
218
        // on a non-TLS 1.3 connection since a parsing error isn't the real issue
219
        // here.
220
0
        if self.protocol_version() != ProtocolVersion::TLSv1_3 {
221
0
            return Err(Error::General(
222
0
                "TLS 1.2 session tickets may not be sent once the handshake has completed".into(),
223
0
            ));
224
0
        }
225
226
0
        let nst = NewSessionTicketPayloadTls13::read_bytes(payload)?;
227
0
        let mut cx = KernelContext {
228
0
            peer_certificates: self.peer_certificates.as_ref(),
229
0
            protocol: self.protocol,
230
0
            quic: &self.quic,
231
0
        };
232
0
        self.state
233
0
            .handle_new_session_ticket(&mut cx, &nst)
234
0
    }
235
}
236
237
pub(crate) trait KernelState: Send + Sync {
238
    /// Update the traffic secret for the specified direction on the connection.
239
    fn update_secrets(&mut self, dir: Direction) -> Result<ConnectionTrafficSecrets, Error>;
240
241
    /// Handle a new session ticket.
242
    ///
243
    /// This will only ever be called for client connections, as [`KernelConnection`]
244
    /// only exposes the relevant API for client connections.
245
    fn handle_new_session_ticket(
246
        &mut self,
247
        cx: &mut KernelContext<'_>,
248
        message: &NewSessionTicketPayloadTls13,
249
    ) -> Result<(), Error>;
250
}
251
252
pub(crate) struct KernelContext<'a> {
253
    pub(crate) peer_certificates: Option<&'a CertificateChain<'static>>,
254
    pub(crate) protocol: Protocol,
255
    pub(crate) quic: &'a Quic,
256
}
257
258
impl KernelContext<'_> {
259
0
    pub(crate) fn is_quic(&self) -> bool {
260
0
        self.protocol == Protocol::Quic
261
0
    }
262
}
263
264
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
265
pub(crate) enum Direction {
266
    Transmit,
267
    Receive,
268
}