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::{DataKind, 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
22pub 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, fips: FipsStatus) -> Self {
31 Self {
32 outputs: ConnectionOutputs::new(fips),
33 send: SendPath::default(),
34 recv: ReceivePath::new(side),
35 }
36 }
37
38 pub fn send_close_notify(&mut self, tls: &mut Vec<u8>) -> Result<(), Error> {
43 self.send.send_close_notify(tls)
44 }
45
46 pub fn is_handshaking(&self) -> bool {
54 !(self.send.may_send_application_data && self.recv.may_receive_application_data)
55 }
56}
57
58impl Deref for CommonState {
59 type Target = ConnectionOutputs;
60
61 fn deref(&self) -> &Self::Target {
62 &self.outputs
63 }
64}
65
66impl DerefMut for CommonState {
67 fn deref_mut(&mut self) -> &mut Self::Target {
68 &mut self.outputs
69 }
70}
71
72impl fmt::Debug for CommonState {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 f.debug_struct("CommonState")
75 .finish_non_exhaustive()
76 }
77}
78
79pub struct ConnectionOutputs {
81 pub(crate) fips: FipsStatus,
82 negotiated_version: Option<ProtocolVersion>,
83 handshake_kind: Option<HandshakeKind>,
84 suite: Option<SupportedCipherSuite>,
85 negotiated_kx_group: Option<&'static dyn SupportedKxGroup>,
86 alpn_protocol: Option<ApplicationProtocol<'static>>,
87 peer_identity: Option<VerifiedIdentity<'static>>,
88 extended_main_secret: Option<bool>,
89 exporter: Option<Box<dyn Exporter>>,
90 pub(crate) early_exporter: Option<Box<dyn Exporter>>,
91}
92
93impl ConnectionOutputs {
94 fn new(fips: FipsStatus) -> Self {
95 Self {
96 fips,
97 negotiated_version: None,
98 handshake_kind: None,
99 suite: None,
100 negotiated_kx_group: None,
101 alpn_protocol: None,
102 peer_identity: None,
103 extended_main_secret: None,
104 exporter: None,
105 early_exporter: None,
106 }
107 }
108
109 pub fn peer_identity(&self) -> Option<&VerifiedIdentity<'static>> {
118 self.peer_identity.as_ref()
119 }
120
121 pub fn alpn_protocol(&self) -> Option<&ApplicationProtocol<'static>> {
127 self.alpn_protocol.as_ref()
128 }
129
130 pub fn negotiated_cipher_suite(&self) -> Option<SupportedCipherSuite> {
134 self.suite
135 }
136
137 pub fn negotiated_key_exchange_group(&self) -> Option<&'static dyn SupportedKxGroup> {
147 self.negotiated_kx_group
148 }
149
150 pub fn protocol_version(&self) -> Option<ProtocolVersion> {
154 self.negotiated_version
155 }
156
157 pub fn fips(&self) -> FipsStatus {
165 self.fips
166 }
167
168 pub fn extended_main_secret(&self) -> Option<bool> {
176 self.extended_main_secret
177 }
178
179 pub fn handshake_kind(&self) -> Option<HandshakeKind> {
186 self.handshake_kind
187 }
188
189 #[doc = include_str!("doc/early_exporter.md")]
190 pub fn early_exporter(&mut self) -> Result<KeyingMaterialExporter, Error> {
191 match self.early_exporter.take() {
192 Some(inner) => Ok(KeyingMaterialExporter { inner }),
193 None => Err(ApiMisuse::ExporterNotAvailable.into()),
194 }
195 }
196
197 #[doc = include_str!("doc/exporter.md")]
198 pub fn exporter(&mut self) -> Result<KeyingMaterialExporter, Error> {
199 match self.exporter.take() {
200 Some(inner) => Ok(KeyingMaterialExporter { inner }),
201 None => Err(ApiMisuse::ExporterNotAvailable.into()),
202 }
203 }
204
205 pub(super) fn into_kernel_parts(self) -> Option<(ProtocolVersion, SupportedCipherSuite)> {
206 let Self {
207 negotiated_version,
208 suite,
209 ..
210 } = self;
211
212 match (negotiated_version, suite) {
213 (Some(version), Some(suite)) => Some((version, suite)),
214 _ => None,
215 }
216 }
217}
218
219impl ConnectionOutput for ConnectionOutputs {
220 fn handle(&mut self, ev: OutputEvent<'_>) {
221 match ev {
222 OutputEvent::ApplicationProtocol(protocol) => {
223 self.alpn_protocol = Some(ApplicationProtocol::from(protocol.as_ref()).to_owned())
224 }
225 OutputEvent::CipherSuite(suite) => self.suite = Some(suite),
226 OutputEvent::EarlyExporter(exporter) => self.early_exporter = Some(exporter),
227 OutputEvent::Exporter(exporter) => self.exporter = Some(exporter),
228 OutputEvent::ExtendedMainSecret(ems) => self.extended_main_secret = Some(ems),
229 OutputEvent::HandshakeKind(hk) => {
230 assert!(self.handshake_kind.is_none());
231 self.handshake_kind = Some(hk);
232 }
233 OutputEvent::KeyExchangeGroup(kxg) => {
234 assert!(self.negotiated_kx_group.is_none());
235 self.negotiated_kx_group = Some(kxg);
236 }
237 OutputEvent::PeerIdentity(identity) => self.peer_identity = Some(identity),
238 OutputEvent::ProtocolVersion(ver) => {
239 self.negotiated_version = Some(ver);
240 }
241 }
242 }
243}
244
245impl fmt::Debug for ConnectionOutputs {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 let Self {
248 fips,
249 negotiated_version,
250 handshake_kind,
251 suite,
252 negotiated_kx_group,
253 alpn_protocol,
254 peer_identity,
255 extended_main_secret,
256 exporter: _,
257 early_exporter: _,
258 } = self;
259 f.debug_struct("ConnectionOutputs")
260 .field("fips", fips)
261 .field("negotiated_version", negotiated_version)
262 .field("handshake_kind", handshake_kind)
263 .field("suite", suite)
264 .field("negotiated_kx_group", negotiated_kx_group)
265 .field("alpn_protocol", alpn_protocol)
266 .field("peer_identity", peer_identity)
267 .field("extended_main_secret", extended_main_secret)
268 .finish_non_exhaustive()
269 }
270}
271
272pub(crate) fn maybe_send_fatal_alert(
274 send: &mut dyn SendOutput,
275 error: &Error,
276 tls: &mut Vec<u8>,
277) -> Result<(), Error> {
278 let Ok(alert) = AlertDescription::try_from(error) else {
279 return Ok(());
280 };
281 send.send_alert(AlertLevel::Fatal, alert, tls)
282}
283
284#[derive(Debug, PartialEq, Clone, Copy)]
286#[non_exhaustive]
287pub enum HandshakeKind {
288 Full,
293
294 FullWithHelloRetryRequest,
300
301 Resumed,
307
308 ResumedWithHelloRetryRequest,
314}
315
316pub(crate) trait Output<'m> {
318 fn emit(&mut self, ev: Event);
319
320 fn output(&mut self, ev: OutputEvent<'_>);
321
322 fn send_msg(&mut self, m: Message<'_>, must_encrypt: bool) -> Result<(), Error>;
323
324 fn quic(&mut self) -> Option<&mut dyn QuicOutput> {
325 None
326 }
327
328 fn received_plaintext(&mut self, _payload: DataKind<Payload<'m>>) {}
329
330 fn start_traffic(&mut self);
331
332 fn receive(&mut self) -> &mut ReceivePath;
333
334 fn send(&mut self) -> &mut dyn SendOutput;
335}
336
337pub(crate) trait ConnectionOutput {
338 fn handle(&mut self, ev: OutputEvent<'_>);
339}
340
341pub(crate) enum Event {
343 EarlyData(EarlyDataEvent),
344 EchStatus(EchStatus),
345 ReceivedServerName(Option<DnsName<'static>>),
346 ResumptionData(Vec<u8>),
347}
348
349pub(crate) enum OutputEvent<'a> {
350 ApplicationProtocol(ApplicationProtocol<'a>),
351 CipherSuite(SupportedCipherSuite),
352 EarlyExporter(Box<dyn Exporter>),
353 Exporter(Box<dyn Exporter>),
354 ExtendedMainSecret(bool),
355 HandshakeKind(HandshakeKind),
356 KeyExchangeGroup(&'static dyn SupportedKxGroup),
357 PeerIdentity(VerifiedIdentity<'static>),
358 ProtocolVersion(ProtocolVersion),
359}
360
361pub(crate) enum EarlyDataEvent {
362 Enable(usize),
364 Start,
366 Finished,
368 Accepted,
370 Rejected,
372}
373
374pub(crate) enum UnborrowedPayload {
379 Unborrowed(Range<usize>),
380 Owned(Vec<u8>),
381}
382
383impl UnborrowedPayload {
384 pub(crate) fn unborrow(locator: &Locator, payload: Payload<'_>) -> Self {
393 match payload {
394 Payload::Borrowed(payload) => Self::Unborrowed(locator.locate(payload)),
395 Payload::Owned(payload) => Self::Owned(payload),
396 }
397 }
398
399 pub(crate) fn reborrow<'b>(self, delocator: &Delocator<'b>) -> Payload<'b> {
406 match self {
407 Self::Unborrowed(range) => Payload::Borrowed(delocator.slice_from_range(&range)),
408 Self::Owned(payload) => Payload::Owned(payload),
409 }
410 }
411}
412
413#[expect(clippy::exhaustive_enums)]
415#[derive(Clone, Copy, Debug, PartialEq)]
416pub enum Side {
417 Client,
419 Server,
421}
422
423#[derive(Copy, Clone, Eq, PartialEq, Debug)]
425#[non_exhaustive]
426pub enum Protocol {
427 Tcp,
429 Quic(quic::Version),
431}
432
433impl Protocol {
434 pub(crate) fn is_quic(&self) -> bool {
435 matches!(self, Self::Quic(_))
436 }
437
438 pub(crate) fn supports_version(&self, version: ProtocolVersion) -> bool {
439 match self {
440 Self::Quic(_) => version == ProtocolVersion::TLSv1_3,
441 Self::Tcp => true,
442 }
443 }
444}
445
446pub(crate) struct HandshakeFlight<'a, const TLS13: bool> {
447 pub(crate) transcript: &'a mut HandshakeHash,
448 body: Vec<u8>,
449}
450
451impl<'a, const TLS13: bool> HandshakeFlight<'a, TLS13> {
452 pub(crate) fn new(transcript: &'a mut HandshakeHash) -> Self {
453 Self {
454 transcript,
455 body: Vec::with_capacity(512),
456 }
457 }
458
459 pub(crate) fn add(&mut self, hs: HandshakeMessagePayload<'_>) {
460 let start_len = self.body.len();
461 hs.encode(&mut self.body);
462 self.transcript
463 .add(&self.body[start_len..]);
464 }
465
466 pub(crate) fn finish(self, output: &mut dyn Output<'_>) -> Result<(), Error> {
467 let m = Message {
468 version: EncodableVersion::Legacy(match TLS13 {
469 true => ProtocolVersion::TLSv1_3,
470 false => ProtocolVersion::TLSv1_2,
471 }),
472 payload: MessagePayload::HandshakeFlight(Payload::new(self.body)),
473 };
474
475 output.send_msg(m, TLS13)
476 }
477}
478
479pub(crate) type HandshakeFlightTls12<'a> = HandshakeFlight<'a, false>;
480pub(crate) type HandshakeFlightTls13<'a> = HandshakeFlight<'a, true>;