1use std::io;
4
5use crate::error::{Error, InvalidMessage, Result};
6use crate::setup::{TlsCryptoInfoRx, TlsCryptoInfoTx};
7
8#[derive(zeroize_derive::ZeroizeOnDrop)]
9pub struct AeadKey<const N: usize>(pub(crate) [u8; N]);
14
15impl<const N: usize> core::fmt::Debug for AeadKey<N> {
16 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17 f.debug_struct("AeadKey").finish()
18 }
19}
20
21impl<const N: usize> AeadKey<N> {
22 #[must_use]
24 pub const fn new(inner: [u8; N]) -> Self {
25 Self(inner)
26 }
27}
28
29impl<const N: usize> From<[u8; N]> for AeadKey<N> {
30 fn from(arr: [u8; N]) -> Self {
31 Self::new(arr)
32 }
33}
34
35#[non_exhaustive]
36pub enum ConnectionTrafficSecrets {
41 Aes128Gcm {
43 key: AeadKey<{ libc::TLS_CIPHER_AES_GCM_128_KEY_SIZE }>,
45
46 iv: [u8; libc::TLS_CIPHER_AES_GCM_128_IV_SIZE],
48
49 salt: [u8; libc::TLS_CIPHER_AES_GCM_128_SALT_SIZE],
51 },
52
53 Aes256Gcm {
55 key: AeadKey<{ libc::TLS_CIPHER_AES_GCM_256_KEY_SIZE }>,
57
58 iv: [u8; libc::TLS_CIPHER_AES_GCM_256_IV_SIZE],
60
61 salt: [u8; libc::TLS_CIPHER_AES_GCM_256_SALT_SIZE],
63 },
64
65 Chacha20Poly1305 {
67 key: AeadKey<{ libc::TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE }>,
69
70 iv: [u8; libc::TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE],
72
73 salt: [u8; libc::TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE],
75 },
76
77 Aes128Ccm {
79 key: AeadKey<{ libc::TLS_CIPHER_AES_CCM_128_KEY_SIZE }>,
81
82 iv: [u8; libc::TLS_CIPHER_AES_CCM_128_IV_SIZE],
84
85 salt: [u8; libc::TLS_CIPHER_AES_CCM_128_SALT_SIZE],
87 },
88
89 Sm4Gcm {
91 key: AeadKey<{ libc::TLS_CIPHER_SM4_GCM_KEY_SIZE }>,
93
94 iv: [u8; libc::TLS_CIPHER_SM4_GCM_IV_SIZE],
96
97 salt: [u8; libc::TLS_CIPHER_SM4_GCM_SALT_SIZE],
99 },
100
101 Sm4Ccm {
103 key: AeadKey<{ libc::TLS_CIPHER_SM4_CCM_KEY_SIZE }>,
105
106 iv: [u8; libc::TLS_CIPHER_SM4_CCM_IV_SIZE],
108
109 salt: [u8; libc::TLS_CIPHER_SM4_CCM_SALT_SIZE],
111 },
112
113 Aria128Gcm {
115 key: AeadKey<{ libc::TLS_CIPHER_ARIA_GCM_128_KEY_SIZE }>,
117
118 iv: [u8; libc::TLS_CIPHER_ARIA_GCM_128_IV_SIZE],
120
121 salt: [u8; libc::TLS_CIPHER_ARIA_GCM_128_SALT_SIZE],
123 },
124
125 Aria256Gcm {
127 key: AeadKey<{ libc::TLS_CIPHER_ARIA_GCM_256_KEY_SIZE }>,
129
130 iv: [u8; libc::TLS_CIPHER_ARIA_GCM_256_IV_SIZE],
132
133 salt: [u8; libc::TLS_CIPHER_ARIA_GCM_256_SALT_SIZE],
135 },
136}
137
138#[allow(clippy::exhaustive_structs)]
139pub struct ExtractedSecrets {
150 pub tx: (u64, ConnectionTrafficSecrets),
152
153 pub rx: (u64, ConnectionTrafficSecrets),
155}
156
157macro_rules! enum_builder {
161 (
162 $(#[doc = $comment:literal])*
163 #[repr($uint:ty)]
164 $enum_vis:vis enum $enum_name:ident
165 {
166 $(
167 $(#[doc = $enum_comment:literal])*
168 $enum_var:ident => $enum_val:literal
169 ),*
170 $(,)?
171 $(
172 !Debug:
173 $(
174 $(#[doc = $enum_comment_no_debug:literal])*
175 $enum_var_no_debug:ident => $enum_val_no_debug:literal
176 ),*
177 $(,)?
178 )?
179 }
180 ) => {
181 $(#[doc = $comment])*
182 #[non_exhaustive]
183 #[allow(missing_docs)]
184 #[derive(PartialEq, Eq, Clone, Copy)]
185 $enum_vis enum $enum_name {
186 $(
187 $(#[doc = $enum_comment])*
188 $enum_var
189 ),*
190 $(
191 ,
192 $(
193 $(#[doc = $enum_comment_no_debug])*
194 $enum_var_no_debug
195 ),*
196 )?
197 ,Unknown($uint)
198 }
199
200 impl $enum_name {
201 #[allow(dead_code)]
203 pub(crate) const fn to_array(self) -> [u8; core::mem::size_of::<$uint>()] {
204 self.to_int().to_be_bytes()
205 }
206
207 #[allow(dead_code)]
209 pub(crate) const fn as_str(&self) -> Option<&'static str> {
210 match self {
211 $( $enum_name::$enum_var => Some(stringify!($enum_var))),*
212 $(, $( $enum_name::$enum_var_no_debug => Some(stringify!($enum_var_no_debug))),* )?
213 ,$enum_name::Unknown(_) => None,
214 }
215 }
216
217 #[allow(dead_code)]
218 pub(crate) const fn from_int(x: $uint) -> Self {
219 match x {
220 $($enum_val => $enum_name::$enum_var),*
221 $(, $($enum_val_no_debug => $enum_name::$enum_var_no_debug),* )?
222 , x => $enum_name::Unknown(x),
223 }
224 }
225
226 #[allow(dead_code)]
227 pub(crate) const fn to_int(self) -> $uint {
228 match self {
229 $( $enum_name::$enum_var => $enum_val),*
230 $(, $( $enum_name::$enum_var_no_debug => $enum_val_no_debug),* )?
231 ,$enum_name::Unknown(x) => x
232 }
233 }
234 }
235
236 impl From<$uint> for $enum_name {
237 fn from(x: $uint) -> Self {
238 Self::from_int(x)
239 }
240 }
241
242 impl From<$enum_name> for $uint {
243 fn from(value: $enum_name) -> Self {
244 value.to_int()
245 }
246 }
247
248 impl core::fmt::Debug for $enum_name {
249 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
250 match self {
251 $( $enum_name::$enum_var => f.write_str(stringify!($enum_var)), )*
252 _ => write!(f, "{}(0x{:x?})", stringify!($enum_name), <$uint>::from(*self)),
253 }
254 }
255 }
256 };
257}
258
259enum_builder! {
260 #[repr(u8)]
264 pub(crate) enum AlertLevel {
265 Warning => 0x01,
266 Fatal => 0x02,
267 }
268}
269
270enum_builder! {
271 #[repr(u8)]
275 pub enum AlertDescription {
276 CloseNotify => 0x00,
277 UnexpectedMessage => 0x0a,
278 BadRecordMac => 0x14,
279 DecryptionFailed => 0x15,
280 RecordOverflow => 0x16,
281 DecompressionFailure => 0x1e,
282 HandshakeFailure => 0x28,
283 NoCertificate => 0x29,
284 BadCertificate => 0x2a,
285 UnsupportedCertificate => 0x2b,
286 CertificateRevoked => 0x2c,
287 CertificateExpired => 0x2d,
288 CertificateUnknown => 0x2e,
289 IllegalParameter => 0x2f,
290 UnknownCa => 0x30,
291 AccessDenied => 0x31,
292 DecodeError => 0x32,
293 DecryptError => 0x33,
294 ExportRestriction => 0x3c,
295 ProtocolVersion => 0x46,
296 InsufficientSecurity => 0x47,
297 InternalError => 0x50,
298 InappropriateFallback => 0x56,
299 UserCanceled => 0x5a,
300 NoRenegotiation => 0x64,
301 MissingExtension => 0x6d,
302 UnsupportedExtension => 0x6e,
303 CertificateUnobtainable => 0x6f,
304 UnrecognizedName => 0x70,
305 BadCertificateStatusResponse => 0x71,
306 BadCertificateHashValue => 0x72,
307 UnknownPskIdentity => 0x73,
308 CertificateRequired => 0x74,
309 NoApplicationProtocol => 0x78,
310 EncryptedClientHelloRequired => 0x79, }
312}
313
314enum_builder! {
315 #[repr(u8)]
319 pub enum ContentType {
320 ChangeCipherSpec => 0x14,
321 Alert => 0x15,
322 Handshake => 0x16,
323 ApplicationData => 0x17,
324 Heartbeat => 0x18,
325 }
326}
327
328enum_builder! {
329 #[repr(u8)]
333 pub(crate) enum KeyUpdateRequest {
334 UpdateNotRequested => 0x00,
335 UpdateRequested => 0x01,
336 }
337}
338
339enum_builder! {
340 #[repr(u8)]
344 pub(crate) enum HandshakeType {
345 HelloRequest => 0x00,
346 ClientHello => 0x01,
347 ServerHello => 0x02,
348 HelloVerifyRequest => 0x03,
349 NewSessionTicket => 0x04,
350 EndOfEarlyData => 0x05,
351 HelloRetryRequest => 0x06,
352 EncryptedExtensions => 0x08,
353 Certificate => 0x0b,
354 ServerKeyExchange => 0x0c,
355 CertificateRequest => 0x0d,
356 ServerHelloDone => 0x0e,
357 CertificateVerify => 0x0f,
358 ClientKeyExchange => 0x10,
359 Finished => 0x14,
360 CertificateURL => 0x15,
361 CertificateStatus => 0x16,
362 KeyUpdate => 0x18,
363 CompressedCertificate => 0x19,
364 MessageHash => 0xfe,
365 }
366}
367
368enum_builder! {
369 #[repr(u16)]
373 pub enum ProtocolVersion {
374 SSLv2 => 0x0002,
375 SSLv3 => 0x0300,
376 TLSv1_0 => 0x0301,
377 TLSv1_1 => 0x0302,
378 TLSv1_2 => 0x0303,
379 TLSv1_3 => 0x0304,
380 DTLSv1_0 => 0xFEFF,
381 DTLSv1_2 => 0xFEFD,
382 DTLSv1_3 => 0xFEFC,
383 }
384}
385
386#[allow(clippy::exhaustive_enums)]
387#[derive(Debug, Clone, Copy, PartialEq, Eq)]
388pub enum Peer {
390 Client,
392
393 Server,
395}
396
397pub trait TlsSession {
403 fn peer(&self) -> Peer;
405
406 fn protocol_version(&self) -> ProtocolVersion;
408
409 fn update_tx_secret(&mut self) -> Result<TlsCryptoInfoTx>;
420
421 fn update_rx_secret(&mut self) -> Result<TlsCryptoInfoRx>;
433
434 fn handle_new_session_ticket(&mut self, _payload: &[u8]) -> Result<()>;
448
449 #[inline]
450 fn handle_unknown_message(&mut self, _content_type: u8, _payload: &[u8]) -> Result<()> {
459 Err(Error::InvalidMessage(InvalidMessage::InvalidContentType))
460 }
461}
462
463#[derive(Debug, Clone, Copy)]
464pub struct DummyTlsSession {
466 peer: Peer,
467 protocol_version: ProtocolVersion,
468}
469
470pub static DUMMY_TLS_13_SESSION_CLIENT: DummyTlsSession = DummyTlsSession {
472 peer: Peer::Client,
473 protocol_version: ProtocolVersion::TLSv1_3,
474};
475
476pub static DUMMY_TLS_13_SESSION_SERVER: DummyTlsSession = DummyTlsSession {
478 peer: Peer::Server,
479 protocol_version: ProtocolVersion::TLSv1_3,
480};
481
482pub static DUMMY_TLS_12_SESSION_CLIENT: DummyTlsSession = DummyTlsSession {
484 peer: Peer::Client,
485 protocol_version: ProtocolVersion::TLSv1_2,
486};
487
488pub static DUMMY_TLS_12_SESSION_SERVER: DummyTlsSession = DummyTlsSession {
490 peer: Peer::Server,
491 protocol_version: ProtocolVersion::TLSv1_2,
492};
493
494impl TlsSession for DummyTlsSession {
495 fn peer(&self) -> Peer {
496 self.peer
497 }
498
499 fn protocol_version(&self) -> ProtocolVersion {
500 self.protocol_version
501 }
502
503 fn update_tx_secret(&mut self) -> Result<TlsCryptoInfoTx> {
504 Err(Error::KeyUpdateFailed(io::Error::other(
505 "Dummy TLS session does not support key updates",
506 )))
507 }
508
509 fn update_rx_secret(&mut self) -> Result<TlsCryptoInfoRx> {
510 Err(Error::KeyUpdateFailed(io::Error::other(
511 "Dummy TLS session does not support key updates",
512 )))
513 }
514
515 fn handle_new_session_ticket(&mut self, _payload: &[u8]) -> Result<()> {
516 Err(Error::HandleNewSessionTicketFailed(io::Error::other(
517 "Dummy TLS session does not support new session tickets",
518 )))
519 }
520}
521
522#[cfg(feature = "_shim")]
523mod shim {
524 #[allow(clippy::wildcard_imports)]
525 use super::*;
526
527 #[cfg(feature = "shim-rustls")]
528 impl TlsSession for rustls::kernel::KernelConnection<rustls::client::ClientConnectionData> {
529 fn peer(&self) -> Peer {
530 Peer::Client
531 }
532
533 fn protocol_version(&self) -> ProtocolVersion {
534 self.protocol_version().into()
535 }
536
537 #[track_caller]
538 fn update_tx_secret(&mut self) -> Result<TlsCryptoInfoTx> {
539 let (seq, secrets) = self
540 .update_tx_secret()
541 .map_err(|e| Error::KeyUpdateFailed(io::Error::other(e)))?;
542
543 TlsCryptoInfoTx::new(
544 self.protocol_version().into(),
545 ConnectionTrafficSecrets::try_from(secrets)?,
546 seq,
547 )
548 }
549
550 #[track_caller]
551 fn update_rx_secret(&mut self) -> Result<TlsCryptoInfoRx> {
552 let (seq, secrets) = self
553 .update_rx_secret()
554 .map_err(|e| Error::KeyUpdateFailed(io::Error::other(e)))?;
555
556 TlsCryptoInfoRx::new(
557 self.protocol_version().into(),
558 ConnectionTrafficSecrets::try_from(secrets)?,
559 seq,
560 )
561 }
562
563 #[track_caller]
564 fn handle_new_session_ticket(&mut self, payload: &[u8]) -> Result<()> {
565 self.handle_new_session_ticket(payload)
566 .map_err(|e| Error::HandleNewSessionTicketFailed(io::Error::other(e)))
567 }
568 }
569
570 #[cfg(feature = "shim-rustls")]
571 impl TlsSession for rustls::kernel::KernelConnection<rustls::server::ServerConnectionData> {
572 fn peer(&self) -> Peer {
573 Peer::Server
574 }
575
576 fn protocol_version(&self) -> ProtocolVersion {
577 self.protocol_version().into()
578 }
579
580 #[track_caller]
581 fn update_tx_secret(&mut self) -> Result<TlsCryptoInfoTx> {
582 let (seq, secrets) = self
583 .update_tx_secret()
584 .map_err(|e| Error::KeyUpdateFailed(io::Error::other(e)))?;
585
586 TlsCryptoInfoTx::new(
587 self.protocol_version().into(),
588 ConnectionTrafficSecrets::try_from(secrets)?,
589 seq,
590 )
591 }
592
593 #[track_caller]
594 fn update_rx_secret(&mut self) -> Result<TlsCryptoInfoRx> {
595 let (seq, secrets) = self
596 .update_rx_secret()
597 .map_err(|e| Error::KeyUpdateFailed(io::Error::other(e)))?;
598
599 TlsCryptoInfoRx::new(
600 self.protocol_version().into(),
601 ConnectionTrafficSecrets::try_from(secrets)?,
602 seq,
603 )
604 }
605
606 fn handle_new_session_ticket(&mut self, _payload: &[u8]) -> Result<()> {
607 Err(Error::HandleNewSessionTicketFailed(io::Error::other(
608 "Server should not receive new session ticket",
609 )))
610 }
611 }
612
613 #[cfg(feature = "shim-rustls")]
614 impl From<rustls::ProtocolVersion> for ProtocolVersion {
615 fn from(value: rustls::ProtocolVersion) -> Self {
616 Self::from_int(value.into())
617 }
618 }
619
620 #[cfg(feature = "shim-rustls")]
621 impl TryFrom<rustls::ExtractedSecrets> for ExtractedSecrets {
622 type Error = Error;
623
624 fn try_from(secrets: rustls::ExtractedSecrets) -> Result<Self, Self::Error> {
629 let rustls::ExtractedSecrets {
630 tx: (seq_tx, secrets_tx),
631 rx: (seq_rx, secrets_rx),
632 } = secrets;
633
634 Ok(Self {
635 tx: (seq_tx, ConnectionTrafficSecrets::try_from(secrets_tx)?),
636 rx: (seq_rx, ConnectionTrafficSecrets::try_from(secrets_rx)?),
637 })
638 }
639 }
640
641 #[cfg(feature = "shim-rustls")]
642 impl TryFrom<rustls::ConnectionTrafficSecrets> for ConnectionTrafficSecrets {
643 type Error = Error;
644
645 #[track_caller]
646 fn try_from(value: rustls::ConnectionTrafficSecrets) -> Result<Self, Self::Error> {
647 match value {
648 rustls::ConnectionTrafficSecrets::Aes128Gcm { key, iv } => Ok(Self::Aes128Gcm {
649 key: AeadKey::new(
650 key.as_ref()
651 .try_into()
652 .expect("key length mismatch"),
653 ),
654 iv: iv.as_ref()[4..]
655 .try_into()
656 .expect("iv length mismatch"),
657 salt: iv.as_ref()[..4]
658 .try_into()
659 .expect("salt length mismatch"),
660 }),
661 rustls::ConnectionTrafficSecrets::Aes256Gcm { key, iv } => Ok(Self::Aes256Gcm {
662 key: AeadKey::new(
663 key.as_ref()
664 .try_into()
665 .expect("key length mismatch"),
666 ),
667 iv: iv.as_ref()[4..]
668 .try_into()
669 .expect("iv length mismatch"),
670 salt: iv.as_ref()[..4]
671 .try_into()
672 .expect("salt length mismatch"),
673 }),
674 rustls::ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv } => {
675 Ok(Self::Chacha20Poly1305 {
676 key: AeadKey::new(
677 key.as_ref()
678 .try_into()
679 .expect("key length mismatch"),
680 ),
681 iv: iv
682 .as_ref()
683 .try_into()
684 .expect("iv length mismatch"),
685 salt: [],
686 })
687 }
688 secrets => Err(Error::CryptoMaterial(io::Error::other(format!(
689 "The given crypto material is not supported by the running kernel: {}",
690 std::any::type_name_of_val(&secrets)
691 )))),
692 }
693 }
694 }
695}