rustls/conn/kernel.rs
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//! # Cipher Suite Confidentiality Limits
16//! Some cipher suites (notably AES-GCM) have vulnerabilities where they are no
17//! longer secure once a certain number of messages have been sent. Normally,
18//! rustls tracks how many messages have been written or read and will
19//! automatically either refresh keys or emit an error when approaching the
20//! confidentiality limit of the cipher suite.
21//!
22//! [`KernelConnection`] has no way to track this. It is the responsibility
23//! of the user of the API to track approximately how many messages have been
24//! sent and either refresh the traffic keys or abort the connection before the
25//! confidentiality limit is reached.
26//!
27//! You can find the current confidentiality limit by looking at
28//! [`CipherSuiteCommon::confidentiality_limit`] for the cipher suite selected
29//! by the connection.
30//!
31//! [`CipherSuiteCommon::confidentiality_limit`]: crate::CipherSuiteCommon::confidentiality_limit
32//! [`KernelConnection`]: crate::kernel::KernelConnection
33
34use alloc::boxed::Box;
35use core::marker::PhantomData;
36
37use crate::client::ClientSide;
38use crate::enums::ProtocolVersion;
39use crate::error::ApiMisuse;
40use crate::msgs::{Codec, NewSessionTicketPayloadTls13};
41use crate::tls13::key_schedule::KeyScheduleTrafficSend;
42use crate::{ConnectionOutputs, ConnectionTrafficSecrets, Error, SupportedCipherSuite};
43
44/// A kernel connection.
45///
46/// This does not directly wrap a kernel connection, rather it gives you the
47/// minimal interfaces you need to implement a well-behaved TLS connection on
48/// top of kTLS.
49///
50/// See the [`crate::kernel`] module docs for more details.
51pub struct KernelConnection<Side> {
52 state: Box<dyn KernelState>,
53 tls13_key_schedule: Option<Box<KeyScheduleTrafficSend>>,
54
55 negotiated_version: ProtocolVersion,
56 suite: SupportedCipherSuite,
57
58 _side: PhantomData<Side>,
59}
60
61impl<Side> KernelConnection<Side> {
62 pub(crate) fn new(
63 state: Box<dyn KernelState>,
64 outputs: ConnectionOutputs,
65 tls13_key_schedule: Option<Box<KeyScheduleTrafficSend>>,
66 ) -> Result<Self, Error> {
67 let (negotiated_version, suite) = outputs
68 .into_kernel_parts()
69 .ok_or(Error::HandshakeNotComplete)?;
70 Ok(Self {
71 state,
72 tls13_key_schedule,
73
74 negotiated_version,
75 suite,
76
77 _side: PhantomData,
78 })
79 }
80
81 /// Retrieves the cipher suite agreed with the peer.
82 pub fn negotiated_cipher_suite(&self) -> SupportedCipherSuite {
83 self.suite
84 }
85
86 /// Retrieves the protocol version agreed with the peer.
87 pub fn protocol_version(&self) -> ProtocolVersion {
88 self.negotiated_version
89 }
90
91 /// Update the traffic secret used for encrypting messages sent to the peer.
92 ///
93 /// Returns the new traffic secret and initial sequence number to use.
94 ///
95 /// In order to use the new secret you should send a TLS 1.3 key update to
96 /// the peer and then use the new traffic secrets to encrypt any future
97 /// messages.
98 ///
99 /// Note that it is only possible to update the traffic secrets on a TLS 1.3
100 /// connection. Attempting to do so on a non-TLS 1.3 connection will result
101 /// in an error.
102 pub fn update_tx_secret(&mut self) -> Result<(u64, ConnectionTrafficSecrets), Error> {
103 match &mut self.tls13_key_schedule {
104 // The sequence number always starts at 0 after a key update.
105 Some(ks) => ks
106 .refresh_traffic_secret()
107 .map(|secret| (0, secret)),
108 None => Err(ApiMisuse::KeyUpdateNotAvailableForTls12.into()),
109 }
110 }
111
112 /// Update the traffic secret used for decrypting messages received from the
113 /// peer.
114 ///
115 /// Returns the new traffic secret and initial sequence number to use.
116 ///
117 /// You should call this method once you receive a TLS 1.3 key update message
118 /// from the peer.
119 ///
120 /// Note that it is only possible to update the traffic secrets on a TLS 1.3
121 /// connection. Attempting to do so on a non-TLS 1.3 connection will result
122 /// in an error.
123 pub fn update_rx_secret(&mut self) -> Result<(u64, ConnectionTrafficSecrets), Error> {
124 // The sequence number always starts at 0 after a key update.
125 self.state
126 .update_rx_secret()
127 .map(|secret| (0, secret))
128 }
129}
130
131impl KernelConnection<ClientSide> {
132 /// Handle a `new_session_ticket` message from the peer.
133 ///
134 /// This will register the session ticket within with rustls so that it can
135 /// be used to establish future TLS connections.
136 ///
137 /// # Getting the right payload
138 ///
139 /// This method expects to be passed the inner payload of the handshake
140 /// message. This means that you will need to parse the header of the
141 /// handshake message in order to determine the correct payload to pass in.
142 /// The message format is described in [RFC 8446 section 4][0]. `payload`
143 /// should not include the `msg_type` or `length` fields.
144 ///
145 /// Code to parse out the payload should look something like this
146 /// ```no_run
147 /// use rustls::enums::{ContentType, HandshakeType};
148 /// use rustls::kernel::KernelConnection;
149 /// use rustls::client::ClientSide;
150 ///
151 /// # fn doctest(conn: &mut KernelConnection<ClientSide>, typ: ContentType, message: &[u8]) -> Result<(), rustls::Error> {
152 /// let conn: &mut KernelConnection<ClientSide> = // ...
153 /// # conn;
154 /// let typ: ContentType = // ...
155 /// # typ;
156 /// let mut message: &[u8] = // ...
157 /// # message;
158 ///
159 /// // Processing for other messages not included in this example
160 /// assert_eq!(typ, ContentType::Handshake);
161 ///
162 /// // There may be multiple handshake payloads within a single handshake message.
163 /// while !message.is_empty() {
164 /// let (typ, len, rest) = match message {
165 /// &[typ, a, b, c, ref rest @ ..] => (
166 /// HandshakeType::from(typ),
167 /// u32::from_be_bytes([0, a, b, c]) as usize,
168 /// rest
169 /// ),
170 /// _ => panic!("error handling not included in this example")
171 /// };
172 ///
173 /// // Processing for other messages not included in this example.
174 /// assert_eq!(typ, HandshakeType::NewSessionTicket);
175 /// assert!(rest.len() >= len, "invalid handshake message");
176 ///
177 /// let (payload, rest) = rest.split_at(len);
178 /// message = rest;
179 ///
180 /// conn.handle_new_session_ticket(payload)?;
181 /// }
182 /// # Ok(())
183 /// # }
184 /// ```
185 ///
186 /// # Errors
187 /// This method will return an error if:
188 /// - This connection is not a TLS 1.3 connection (in TLS 1.2 session tickets
189 /// are sent as part of the handshake).
190 /// - The provided payload is not a valid `new_session_ticket` payload or has
191 /// extra unparsed trailing data.
192 /// - An error occurs while the connection updates the session ticket store.
193 ///
194 /// [0]: https://datatracker.ietf.org/doc/html/rfc8446#section-4
195 pub fn handle_new_session_ticket(&mut self, payload: &[u8]) -> Result<(), Error> {
196 // We want to return a more specific error here first if this is called
197 // on a non-TLS 1.3 connection since a parsing error isn't the real issue
198 // here.
199 if self.protocol_version() != ProtocolVersion::TLSv1_3 {
200 return Err(Error::General(
201 "TLS 1.2 session tickets may not be sent once the handshake has completed".into(),
202 ));
203 }
204
205 let nst = NewSessionTicketPayloadTls13::read_bytes(payload)?;
206 self.state
207 .handle_new_session_ticket(&nst)
208 }
209}
210
211pub(crate) trait KernelState: Send + Sync {
212 /// Update the traffic secret for the specified direction on the connection.
213 fn update_rx_secret(&mut self) -> Result<ConnectionTrafficSecrets, Error>;
214
215 /// Handle a new session ticket.
216 ///
217 /// This will only ever be called for client connections, as [`KernelConnection`]
218 /// only exposes the relevant API for client connections.
219 fn handle_new_session_ticket(
220 &self,
221 message: &NewSessionTicketPayloadTls13,
222 ) -> Result<(), Error>;
223}