1use crate::quantum_crypto::{MlKemSecurityLevel, QuantumKeyExchange, SharedSecret};
12use crate::traffic_obfuscation::{TrafficObfuscationConfig, TrafficObfuscator};
13use crate::types::{ConnectionStatus, NetworkError, PeerId};
14use dashmap::DashMap;
16use parking_lot::RwLock as ParkingRwLock;
17use quinn::Endpoint;
18use rustls::{ClientConfig, ServerConfig};
19use std::net::SocketAddr;
20use std::pin::Pin;
21use std::sync::Arc;
22use std::time::{Duration, Instant};
23use thiserror::Error;
24use tokio::io::{AsyncRead, AsyncWrite};
25use tokio::net::{TcpListener, TcpStream};
26use tokio::sync::Mutex;
27use tokio::time::timeout;
28use tracing::{debug, error, info, warn};
29
30#[derive(Debug, Error)]
32pub enum TransportError {
33 #[error("Connection failed: {0}")]
35 ConnectionFailed(String),
36
37 #[error("Read error: {0}")]
39 ReadError(String),
40
41 #[error("Write error: {0}")]
43 WriteError(String),
44
45 #[error("TLS error: {0}")]
47 TlsError(String),
48
49 #[error("Post-quantum crypto error: {0}")]
51 PostQuantumError(String),
52
53 #[error("Handshake timeout after {0:?}")]
55 HandshakeTimeout(Duration),
56
57 #[error("Invalid certificate: {0}")]
59 InvalidCertificate(String),
60
61 #[error("Connection limit exceeded: {current}/{max}")]
63 ConnectionLimitExceeded { current: usize, max: usize },
64
65 #[error("Invalid message format: {0}")]
67 InvalidMessageFormat(String),
68
69 #[error("Encryption error: {0}")]
71 EncryptionError(String),
72
73 #[error("Configuration error: {0}")]
75 ConfigurationError(String),
76
77 #[error("Network error: {0}")]
79 NetworkError(#[from] NetworkError),
80
81 #[error("IO error: {0}")]
83 IoError(#[from] std::io::Error),
84
85 #[error("Rustls error: {0}")]
87 RustlsError(#[from] rustls::Error),
88
89 #[error("Quinn error: {0}")]
91 QuinnError(#[from] quinn::ConnectionError),
92}
93
94#[derive(Debug, Clone)]
96pub struct TransportConfig {
97 pub use_tls: bool,
99
100 pub use_post_quantum: bool,
102
103 pub cert_path: Option<String>,
105
106 pub key_path: Option<String>,
108
109 pub ca_cert_path: Option<String>,
111
112 pub max_connections: usize,
114
115 pub connection_timeout: Duration,
117
118 pub handshake_timeout: Duration,
120
121 pub use_quic: bool,
123
124 pub ml_kem_security_level: MlKemSecurityLevel,
126
127 pub enable_connection_pooling: bool,
129
130 pub max_message_size: usize,
132
133 pub enable_compression: bool,
135
136 pub buffer_size: usize,
138
139 pub enable_traffic_obfuscation: bool,
141
142 pub traffic_obfuscation_config: TrafficObfuscationConfig,
144}
145
146impl Default for TransportConfig {
147 fn default() -> Self {
148 Self {
149 use_tls: true,
150 use_post_quantum: true,
151 cert_path: None,
152 key_path: None,
153 ca_cert_path: None,
154 max_connections: 1000,
155 connection_timeout: Duration::from_secs(30),
156 handshake_timeout: Duration::from_secs(10),
157 use_quic: false,
158 ml_kem_security_level: MlKemSecurityLevel::Level768,
159 enable_connection_pooling: true,
160 max_message_size: 16 * 1024 * 1024, enable_compression: false,
162 buffer_size: 64 * 1024, enable_traffic_obfuscation: true,
164 traffic_obfuscation_config: TrafficObfuscationConfig::default(),
165 }
166 }
167}
168
169pub trait AsyncTransport: AsyncRead + AsyncWrite + Send + Sync + Unpin {
171 fn peer_addr(&self) -> Result<SocketAddr, TransportError>;
173
174 fn local_addr(&self) -> Result<SocketAddr, TransportError>;
176
177 fn is_secure(&self) -> bool;
179
180 fn metadata(&self) -> ConnectionMetadata;
182
183 fn close_sync(&mut self) -> Result<(), TransportError>;
185}
186
187#[derive(Debug, Clone)]
189pub struct ConnectionMetadata {
190 pub connection_id: String,
192 pub peer_id: Option<PeerId>,
194 pub status: ConnectionStatus,
196 pub established_at: Instant,
198 pub last_activity: Instant,
200 pub bytes_sent: u64,
202 pub bytes_received: u64,
204 pub is_post_quantum: bool,
206 pub tls_version: Option<String>,
208}
209
210#[async_trait::async_trait]
212pub trait Transport: Send + Sync {
213 async fn init(&mut self, config: TransportConfig) -> Result<(), TransportError>;
215
216 async fn listen(&mut self, addr: SocketAddr) -> Result<(), TransportError>;
218
219 async fn connect(
221 &mut self,
222 addr: SocketAddr,
223 ) -> Result<Box<dyn AsyncTransport + Send + Sync>, TransportError>;
224
225 async fn accept(&mut self) -> Result<Box<dyn AsyncTransport + Send + Sync>, TransportError>;
227
228 async fn close_connection(&mut self, connection_id: &str) -> Result<(), TransportError>;
230
231 fn get_connections(&self) -> Vec<ConnectionMetadata>;
233
234 fn get_stats(&self) -> TransportStats;
236
237 async fn shutdown(&mut self) -> Result<(), TransportError>;
239}
240
241#[derive(Debug, Clone, Default)]
243pub struct TransportStats {
244 pub total_connections: u64,
246 pub active_connections: usize,
248 pub total_bytes_sent: u64,
250 pub total_bytes_received: u64,
252 pub connection_errors: u64,
254 pub handshake_failures: u64,
256 pub post_quantum_handshakes: u64,
258 pub avg_connection_duration: Duration,
260}
261
262pub struct SecureTransport {
264 config: TransportConfig,
266 listener: Option<Arc<Mutex<TcpListener>>>,
268 quic_endpoint: Option<Arc<Mutex<Endpoint>>>,
270 connections: Arc<DashMap<String, Arc<Mutex<Box<dyn AsyncTransport + Send + Sync>>>>>,
272 connection_metadata: Arc<DashMap<String, ConnectionMetadata>>,
274 tls_client_config: Option<Arc<ClientConfig>>,
276 #[allow(dead_code)]
278 tls_server_config: Option<Arc<ServerConfig>>,
279 quantum_kex: Arc<Mutex<QuantumKeyExchange>>,
281 stats: Arc<ParkingRwLock<TransportStats>>,
283 connection_counter: Arc<std::sync::atomic::AtomicU64>,
285 traffic_obfuscator: Option<Arc<TrafficObfuscator>>,
289}
290
291unsafe impl Send for SecureTransport {}
293unsafe impl Sync for SecureTransport {}
294
295impl SecureTransport {
296 pub fn new() -> Self {
298 Self {
299 config: TransportConfig::default(),
300 listener: None,
301 quic_endpoint: None,
302 connections: Arc::new(DashMap::new()),
303 connection_metadata: Arc::new(DashMap::new()),
304 tls_client_config: None,
305 tls_server_config: None,
306 quantum_kex: Arc::new(Mutex::new(QuantumKeyExchange::with_security_level(
307 MlKemSecurityLevel::Level768,
308 ))),
309 stats: Arc::new(ParkingRwLock::new(TransportStats::default())),
310 connection_counter: Arc::new(std::sync::atomic::AtomicU64::new(0)),
311 traffic_obfuscator: None,
313 }
314 }
315
316 pub fn with_config(config: TransportConfig) -> Self {
318 let mut transport = Self::new();
319 transport.config = config.clone();
320 transport.quantum_kex = Arc::new(Mutex::new(QuantumKeyExchange::with_security_level(
321 config.ml_kem_security_level,
322 )));
323
324 if config.enable_traffic_obfuscation {
326 transport.traffic_obfuscator = Some(Arc::new(TrafficObfuscator::new(
327 config.traffic_obfuscation_config.clone(),
328 )));
329 }
330
331 transport
332 }
333
334 async fn setup_tls_config(&mut self) -> Result<(), TransportError> {
336 if !self.config.use_tls {
337 return Ok(());
338 }
339
340 info!("Setting up TLS configuration");
341
342 let client_config = ClientConfig::builder()
344 .with_root_certificates(self.load_ca_certificates()?)
345 .with_no_client_auth();
346
347 if self.config.use_post_quantum {
349 debug!("Enabling post-quantum cipher suites");
350 }
353
354 self.tls_client_config = Some(Arc::new(client_config));
355
356 info!("TLS configuration completed successfully");
360 Ok(())
361 }
362
363 fn load_ca_certificates(&self) -> Result<rustls::RootCertStore, TransportError> {
365 let mut root_store = rustls::RootCertStore::empty();
366
367 root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
369
370 Ok(root_store)
371 }
372
373 #[allow(dead_code)]
375 fn load_certificate_chain(
376 &self,
377 _cert_path: &str,
378 ) -> Result<Vec<rustls::pki_types::CertificateDer<'static>>, TransportError> {
379 Err(TransportError::ConfigurationError(
381 "Certificate loading not implemented".to_string(),
382 ))
383 }
384
385 #[allow(dead_code)]
387 fn load_private_key(
388 &self,
389 _key_path: &str,
390 ) -> Result<rustls::pki_types::PrivateKeyDer<'static>, TransportError> {
391 Err(TransportError::ConfigurationError(
393 "Private key loading not implemented".to_string(),
394 ))
395 }
396
397 async fn setup_quic_endpoint(&mut self) -> Result<(), TransportError> {
399 if !self.config.use_quic {
400 return Ok(());
401 }
402
403 info!("Setting up QUIC endpoint");
404
405 warn!("QUIC support not yet implemented");
408
409 Ok(())
410 }
411
412 fn generate_connection_id(&self) -> String {
414 let id = self
415 .connection_counter
416 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
417 format!("conn_{}", id)
418 }
419
420 #[allow(dead_code)]
422 async fn perform_post_quantum_handshake(&self) -> Result<SharedSecret, TransportError> {
423 if !self.config.use_post_quantum {
424 return Err(TransportError::PostQuantumError(
425 "Post-quantum crypto disabled".to_string(),
426 ));
427 }
428
429 debug!("Performing post-quantum handshake (placeholder)");
430
431 let dummy_secret = crate::quantum_crypto::SharedSecret {
434 secret: vec![0u8; 32],
435 };
436
437 Ok(dummy_secret)
438 }
439
440 #[allow(dead_code)]
442 fn update_stats(&self, bytes_sent: u64, bytes_received: u64) {
443 let mut stats = self.stats.write();
444 stats.total_bytes_sent += bytes_sent;
445 stats.total_bytes_received += bytes_received;
446 }
447
448 #[allow(dead_code)]
450 async fn cleanup_connections(&self) {
451 let now = Instant::now();
452 let timeout = Duration::from_secs(300); let mut to_remove = Vec::new();
455
456 for entry in self.connection_metadata.iter() {
457 let metadata = entry.value();
458 if now.duration_since(metadata.last_activity) > timeout {
459 to_remove.push(entry.key().clone());
460 }
461 }
462
463 for conn_id in to_remove {
464 debug!("Cleaning up inactive connection: {}", conn_id);
465 self.connections.remove(&conn_id);
466 self.connection_metadata.remove(&conn_id);
467 }
468 }
469
470 }
491
492#[async_trait::async_trait]
493impl Transport for SecureTransport {
494 async fn init(&mut self, config: TransportConfig) -> Result<(), TransportError> {
495 info!("Initializing secure transport with config: {:?}", config);
496
497 self.config = config.clone();
498
499 self.setup_tls_config().await?;
501
502 self.setup_quic_endpoint().await?;
504
505 if self.config.use_post_quantum {
507 let mut quantum_kex = self.quantum_kex.lock().await;
508 quantum_kex.initialize().map_err(|e| {
509 TransportError::PostQuantumError(format!("Failed to initialize quantum KEX: {}", e))
510 })?;
511 }
512
513 if config.enable_traffic_obfuscation {
515 let obfuscator = Arc::new(TrafficObfuscator::new(
516 config.traffic_obfuscation_config.clone(),
517 ));
518 obfuscator.start().await;
519 self.traffic_obfuscator = Some(obfuscator);
520 info!("Traffic obfuscation enabled");
521 }
522
523 info!("Secure transport initialized successfully");
524 Ok(())
525 }
526
527 async fn listen(&mut self, addr: SocketAddr) -> Result<(), TransportError> {
528 info!("Starting to listen on address: {}", addr);
529
530 let listener = TcpListener::bind(addr).await.map_err(|e| {
531 TransportError::ConnectionFailed(format!("Failed to bind to {}: {}", addr, e))
532 })?;
533
534 self.listener = Some(Arc::new(Mutex::new(listener)));
535
536 info!("Successfully listening on {}", addr);
537 Ok(())
538 }
539
540 async fn connect(
541 &mut self,
542 addr: SocketAddr,
543 ) -> Result<Box<dyn AsyncTransport + Send + Sync>, TransportError> {
544 debug!("Connecting to {}", addr);
545
546 if self.connections.len() >= self.config.max_connections {
548 return Err(TransportError::ConnectionLimitExceeded {
549 current: self.connections.len(),
550 max: self.config.max_connections,
551 });
552 }
553
554 let tcp_stream = timeout(self.config.connection_timeout, TcpStream::connect(addr))
556 .await
557 .map_err(|_| TransportError::HandshakeTimeout(self.config.connection_timeout))?
558 .map_err(|e| {
559 TransportError::ConnectionFailed(format!("TCP connection failed: {}", e))
560 })?;
561
562 let transport = TcpTransport::new(tcp_stream, self.generate_connection_id());
565
566 let conn_id = transport.metadata().connection_id.clone();
568 let metadata = transport.metadata();
569
570 self.connection_metadata.insert(conn_id.clone(), metadata);
571
572 let mut stats = self.stats.write();
574 stats.total_connections += 1;
575 stats.active_connections = self.connections.len();
576
577 info!("Successfully connected to {} (conn_id: {})", addr, conn_id);
578
579 Ok(Box::new(transport))
580 }
581
582 async fn accept(&mut self) -> Result<Box<dyn AsyncTransport + Send + Sync>, TransportError> {
583 let listener = self.listener.as_ref().ok_or_else(|| {
584 TransportError::ConfigurationError("Transport not listening".to_string())
585 })?;
586
587 let (tcp_stream, peer_addr) = listener.lock().await.accept().await.map_err(|e| {
589 TransportError::ConnectionFailed(format!("Failed to accept connection: {}", e))
590 })?;
591
592 debug!("Accepted connection from {}", peer_addr);
593
594 if self.connections.len() >= self.config.max_connections {
596 return Err(TransportError::ConnectionLimitExceeded {
597 current: self.connections.len(),
598 max: self.config.max_connections,
599 });
600 }
601
602 let transport = TcpTransport::new(tcp_stream, self.generate_connection_id());
604
605 let conn_id = transport.metadata().connection_id.clone();
607 let metadata = transport.metadata();
608
609 self.connection_metadata.insert(conn_id.clone(), metadata);
610
611 let mut stats = self.stats.write();
613 stats.total_connections += 1;
614 stats.active_connections = self.connections.len();
615
616 info!(
617 "Successfully accepted connection from {} (conn_id: {})",
618 peer_addr, conn_id
619 );
620
621 Ok(Box::new(transport))
622 }
623
624 async fn close_connection(&mut self, connection_id: &str) -> Result<(), TransportError> {
625 debug!("Closing connection: {}", connection_id);
626
627 if let Some((_, transport)) = self.connections.remove(connection_id) {
628 let mut transport = transport.lock().await;
629 transport.close_sync()?;
630 }
631
632 self.connection_metadata.remove(connection_id);
633
634 let mut stats = self.stats.write();
636 stats.active_connections = self.connections.len();
637
638 info!("Connection {} closed successfully", connection_id);
639 Ok(())
640 }
641
642 fn get_connections(&self) -> Vec<ConnectionMetadata> {
643 self.connection_metadata
644 .iter()
645 .map(|entry| entry.value().clone())
646 .collect()
647 }
648
649 fn get_stats(&self) -> TransportStats {
650 self.stats.read().clone()
651 }
652
653 async fn shutdown(&mut self) -> Result<(), TransportError> {
654 info!("Shutting down secure transport");
655
656 let connection_ids: Vec<String> = self
658 .connections
659 .iter()
660 .map(|entry| entry.key().clone())
661 .collect();
662
663 for conn_id in connection_ids.iter() {
664 if let Err(e) = self.close_connection(conn_id).await {
665 warn!("Error closing connection {}: {}", conn_id, e);
666 }
667 }
668
669 self.listener = None;
671
672 if let Some(endpoint) = self.quic_endpoint.take() {
674 endpoint.lock().await.close(0u32.into(), b"shutdown");
675 }
676
677 info!("Secure transport shutdown completed");
678 Ok(())
679 }
680}
681
682struct TcpTransport {
684 stream: TcpStream,
685 #[allow(dead_code)]
686 connection_id: String,
687 metadata: ConnectionMetadata,
688}
689
690unsafe impl Send for TcpTransport {}
692unsafe impl Sync for TcpTransport {}
693
694impl TcpTransport {
695 fn new(stream: TcpStream, connection_id: String) -> Self {
696 let metadata = ConnectionMetadata {
697 connection_id: connection_id.clone(),
698 peer_id: None,
699 status: ConnectionStatus::Connected,
700 established_at: Instant::now(),
701 last_activity: Instant::now(),
702 bytes_sent: 0,
703 bytes_received: 0,
704 is_post_quantum: false,
705 tls_version: None,
706 };
707
708 Self {
709 stream,
710 connection_id,
711 metadata,
712 }
713 }
714}
715
716impl AsyncRead for TcpTransport {
717 fn poll_read(
718 mut self: Pin<&mut Self>,
719 cx: &mut std::task::Context<'_>,
720 buf: &mut tokio::io::ReadBuf<'_>,
721 ) -> std::task::Poll<std::io::Result<()>> {
722 Pin::new(&mut self.stream).poll_read(cx, buf)
723 }
724}
725
726impl AsyncWrite for TcpTransport {
727 fn poll_write(
728 mut self: Pin<&mut Self>,
729 cx: &mut std::task::Context<'_>,
730 buf: &[u8],
731 ) -> std::task::Poll<Result<usize, std::io::Error>> {
732 Pin::new(&mut self.stream).poll_write(cx, buf)
733 }
734
735 fn poll_flush(
736 mut self: Pin<&mut Self>,
737 cx: &mut std::task::Context<'_>,
738 ) -> std::task::Poll<Result<(), std::io::Error>> {
739 Pin::new(&mut self.stream).poll_flush(cx)
740 }
741
742 fn poll_shutdown(
743 mut self: Pin<&mut Self>,
744 cx: &mut std::task::Context<'_>,
745 ) -> std::task::Poll<Result<(), std::io::Error>> {
746 Pin::new(&mut self.stream).poll_shutdown(cx)
747 }
748}
749
750impl AsyncTransport for TcpTransport {
751 fn peer_addr(&self) -> Result<SocketAddr, TransportError> {
752 self.stream.peer_addr().map_err(|e| {
753 TransportError::ConnectionFailed(format!("Failed to get peer address: {}", e))
754 })
755 }
756
757 fn local_addr(&self) -> Result<SocketAddr, TransportError> {
758 self.stream.local_addr().map_err(|e| {
759 TransportError::ConnectionFailed(format!("Failed to get local address: {}", e))
760 })
761 }
762
763 fn is_secure(&self) -> bool {
764 false
765 }
766
767 fn metadata(&self) -> ConnectionMetadata {
768 self.metadata.clone()
769 }
770
771 fn close_sync(&mut self) -> Result<(), TransportError> {
772 Ok(())
775 }
776}
777
778#[derive(Debug, Clone)]
780pub struct SecureFrame {
781 pub length: u32,
783 pub frame_type: u8,
785 pub sequence: u64,
787 pub payload: Vec<u8>,
789 pub auth_tag: [u8; 16],
791}
792
793impl SecureFrame {
794 pub const MAX_FRAME_SIZE: u32 = 16 * 1024 * 1024;
796
797 pub const HEADER_SIZE: usize = 4 + 1 + 8 + 16; pub fn new(frame_type: u8, sequence: u64, payload: Vec<u8>) -> Self {
802 Self {
803 length: payload.len() as u32,
804 frame_type,
805 sequence,
806 payload,
807 auth_tag: [0u8; 16],
808 }
809 }
810
811 pub fn to_bytes(&self) -> Vec<u8> {
813 let mut bytes = Vec::with_capacity(Self::HEADER_SIZE + self.payload.len());
814 bytes.extend_from_slice(&self.length.to_be_bytes());
815 bytes.push(self.frame_type);
816 bytes.extend_from_slice(&self.sequence.to_be_bytes());
817 bytes.extend_from_slice(&self.payload);
818 bytes.extend_from_slice(&self.auth_tag);
819 bytes
820 }
821
822 pub fn from_bytes(bytes: &[u8]) -> Result<Self, TransportError> {
824 if bytes.len() < Self::HEADER_SIZE {
825 return Err(TransportError::InvalidMessageFormat(
826 "Frame too short".to_string(),
827 ));
828 }
829
830 let length = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
831
832 if length > Self::MAX_FRAME_SIZE {
833 return Err(TransportError::InvalidMessageFormat(
834 "Frame too large".to_string(),
835 ));
836 }
837
838 let frame_type = bytes[4];
839 let sequence = u64::from_be_bytes([
840 bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12],
841 ]);
842
843 let payload_end = 13 + length as usize;
844 if bytes.len() < payload_end + 16 {
845 return Err(TransportError::InvalidMessageFormat(
846 "Invalid frame length".to_string(),
847 ));
848 }
849
850 let payload = bytes[13..payload_end].to_vec();
851 let mut auth_tag = [0u8; 16];
852 auth_tag.copy_from_slice(&bytes[payload_end..payload_end + 16]);
853
854 Ok(Self {
855 length,
856 frame_type,
857 sequence,
858 payload,
859 auth_tag,
860 })
861 }
862}
863
864pub mod utils {
866 use super::*;
867
868 pub fn default_config() -> TransportConfig {
870 TransportConfig::default()
871 }
872
873 pub fn test_config() -> TransportConfig {
875 TransportConfig {
876 use_tls: false,
877 use_post_quantum: false,
878 max_connections: 100,
879 connection_timeout: Duration::from_secs(5),
880 handshake_timeout: Duration::from_secs(3),
881 ..Default::default()
882 }
883 }
884
885 pub fn production_config() -> TransportConfig {
887 TransportConfig {
888 use_tls: true,
889 use_post_quantum: true,
890 max_connections: 10000,
891 connection_timeout: Duration::from_secs(30),
892 handshake_timeout: Duration::from_secs(10),
893 ml_kem_security_level: MlKemSecurityLevel::Level768,
894 enable_connection_pooling: true,
895 max_message_size: 64 * 1024 * 1024, buffer_size: 128 * 1024, ..Default::default()
898 }
899 }
900
901 }
924
925#[cfg(test)]
926mod tests {
927 use super::*;
928
929 #[tokio::test]
930 async fn test_transport_initialization() {
931 let mut transport = SecureTransport::new();
932 let config = utils::test_config();
933
934 let result = transport.init(config).await;
935 assert!(result.is_ok());
936 }
937
938 #[tokio::test]
939 async fn test_secure_frame() {
940 let payload = b"test payload".to_vec();
941 let frame = SecureFrame::new(1, 42, payload.clone());
942
943 let bytes = frame.to_bytes();
944 let decoded = SecureFrame::from_bytes(&bytes).unwrap();
945
946 assert_eq!(decoded.frame_type, 1);
947 assert_eq!(decoded.sequence, 42);
948 assert_eq!(decoded.payload, payload);
949 }
950
951 #[test]
952 fn test_transport_config_default() {
953 let config = TransportConfig::default();
954 assert!(config.use_tls);
955 assert!(config.use_post_quantum);
956 assert_eq!(config.max_connections, 1000);
957 }
958
959 #[test]
960 fn test_connection_metadata() {
961 let metadata = ConnectionMetadata {
962 connection_id: "test_conn".to_string(),
963 peer_id: Some(PeerId::random()),
964 status: ConnectionStatus::Connected,
965 established_at: Instant::now(),
966 last_activity: Instant::now(),
967 bytes_sent: 1024,
968 bytes_received: 2048,
969 is_post_quantum: true,
970 tls_version: Some("TLS 1.3".to_string()),
971 };
972
973 assert_eq!(metadata.connection_id, "test_conn");
974 assert!(metadata.is_post_quantum);
975 assert_eq!(metadata.tls_version, Some("TLS 1.3".to_string()));
976 }
977}