/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 | | } |