use std::sync::Arc;
use std::time::{Duration, Instant};
use dimpl::{Dtls, Output};
use crate::common::*;
#[test]
#[cfg(feature = "rcgen")]
fn dtls13_application_data_exchange() {
use dimpl::certificate::generate_self_signed_certificate;
let _ = env_logger::try_init();
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = dtls13_config();
let mut now = Instant::now();
let mut client = Dtls::new_13(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_13(config, server_cert, now);
server.set_active(false);
let client_data = b"Hello from DTLS 1.3 client!";
let server_data = b"Hello from DTLS 1.3 server!";
let mut client_connected = false;
let mut server_connected = false;
let mut client_received: Vec<u8> = Vec::new();
let mut server_received: Vec<u8> = Vec::new();
let mut client_sent = false;
let mut server_sent = false;
for _ in 0..50 {
client.handle_timeout(now).expect("client timeout");
server.handle_timeout(now).expect("server timeout");
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
if client_out.connected {
client_connected = true;
}
if server_out.connected {
server_connected = true;
}
for data in client_out.app_data {
client_received.extend_from_slice(&data);
}
for data in server_out.app_data {
server_received.extend_from_slice(&data);
}
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_connected && !client_sent {
client
.send_application_data(client_data)
.expect("client send");
client_sent = true;
}
if server_connected && !server_sent {
server
.send_application_data(server_data)
.expect("server send");
server_sent = true;
}
if !client_received.is_empty() && !server_received.is_empty() {
break;
}
now += Duration::from_millis(10);
}
assert!(client_connected, "Client should be connected");
assert!(server_connected, "Server should be connected");
assert_eq!(
client_received, server_data,
"Client should receive server's data"
);
assert_eq!(
server_received, client_data,
"Server should receive client's data"
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls13_multiple_application_data_messages() {
use dimpl::certificate::generate_self_signed_certificate;
let _ = env_logger::try_init();
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = dtls13_config();
let mut now = Instant::now();
let mut client = Dtls::new_13(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_13(config, server_cert, now);
server.set_active(false);
for _ in 0..30 {
client.handle_timeout(now).expect("client timeout");
server.handle_timeout(now).expect("server timeout");
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_out.connected && server_out.connected {
break;
}
now += Duration::from_millis(10);
}
let messages = vec![
b"Message 1".to_vec(),
b"Message 2".to_vec(),
b"Message 3 is a bit longer".to_vec(),
b"Message 4".to_vec(),
b"Message 5 - the final one".to_vec(),
];
for msg in &messages {
client.send_application_data(msg).expect("client send");
}
let mut server_received: Vec<Vec<u8>> = Vec::new();
for _ in 0..20 {
let client_out = drain_outputs(&mut client);
deliver_packets(&client_out.packets, &mut server);
let server_out = drain_outputs(&mut server);
for data in server_out.app_data {
server_received.push(data);
}
if server_received.len() >= messages.len() {
break;
}
now += Duration::from_millis(10);
}
let expected: Vec<u8> = messages.iter().flatten().copied().collect();
let received: Vec<u8> = server_received.iter().flatten().copied().collect();
assert_eq!(received, expected, "All messages should be received");
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls13_bidirectional_data_exchange() {
use dimpl::certificate::generate_self_signed_certificate;
let _ = env_logger::try_init();
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = dtls13_config();
let mut now = Instant::now();
let mut client = Dtls::new_13(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_13(config, server_cert, now);
server.set_active(false);
for _ in 0..30 {
client.handle_timeout(now).expect("client timeout");
server.handle_timeout(now).expect("server timeout");
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_out.connected && server_out.connected {
break;
}
now += Duration::from_millis(10);
}
let rounds = 10;
let mut client_received_count = 0;
let mut server_received_count = 0;
for i in 0..rounds {
let client_msg = format!("Client message {}", i);
let server_msg = format!("Server message {}", i);
client
.send_application_data(client_msg.as_bytes())
.expect("client send");
server
.send_application_data(server_msg.as_bytes())
.expect("server send");
for _ in 0..10 {
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
client_received_count += client_out.app_data.len();
server_received_count += server_out.app_data.len();
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
now += Duration::from_millis(5);
}
}
assert_eq!(
client_received_count, rounds,
"Client should receive all server messages"
);
assert_eq!(
server_received_count, rounds,
"Server should receive all client messages"
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls13_many_small_messages() {
use dimpl::certificate::generate_self_signed_certificate;
let _ = env_logger::try_init();
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = dtls13_config();
let mut now = Instant::now();
let mut client = Dtls::new_13(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_13(config, server_cert, now);
server.set_active(false);
for _ in 0..30 {
client.handle_timeout(now).expect("client timeout");
server.handle_timeout(now).expect("server timeout");
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_out.connected && server_out.connected {
break;
}
now += Duration::from_millis(10);
}
let message_count = 100;
for i in 0..message_count {
let msg = format!("M{}", i);
client.send_application_data(msg.as_bytes()).expect("send");
}
let mut received_bytes: Vec<u8> = Vec::new();
for _ in 0..50 {
let client_out = drain_outputs(&mut client);
deliver_packets(&client_out.packets, &mut server);
let server_out = drain_outputs(&mut server);
for data in server_out.app_data {
received_bytes.extend_from_slice(&data);
}
now += Duration::from_millis(10);
}
assert!(
!received_bytes.is_empty(),
"Should receive application data"
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls13_piggybacks_app_data_with_finished() {
use dimpl::certificate::generate_self_signed_certificate;
let _ = env_logger::try_init();
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = dtls13_config();
let mut now = Instant::now();
let mut client = Dtls::new_13(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_13(config, server_cert, now);
server.set_active(false);
let mut client_connected = false;
let mut server_connected = false;
let mut server_received_early_data = false;
let mut packets_after_finished_sent = 0;
let mut finished_sent = false;
client
.send_application_data(b"Early piggybacked data!")
.expect("queue early data");
eprintln!("Queued early application data before handshake");
for round in 0..50 {
client.handle_timeout(now).expect("client timeout");
server.handle_timeout(now).expect("server timeout");
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
if client_out.connected && !finished_sent {
finished_sent = true;
eprintln!("Round {}: Client sent Finished (connected event)", round);
}
if finished_sent && !server_received_early_data {
packets_after_finished_sent += client_out.packets.len();
}
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if !server_out.app_data.is_empty() {
server_received_early_data = true;
let received = String::from_utf8_lossy(&server_out.app_data[0]);
eprintln!(
"Round {}: Server received early data: '{}' (packets since Finished: {})",
round, received, packets_after_finished_sent
);
assert_eq!(
&server_out.app_data[0][..],
b"Early piggybacked data!",
"Should receive the queued early data"
);
}
client_connected |= client_out.connected;
server_connected |= server_out.connected;
if client_connected && server_connected && server_received_early_data {
break;
}
now += Duration::from_millis(10);
}
assert!(client_connected, "Client should connect");
assert!(server_connected, "Server should connect");
assert!(
server_received_early_data,
"Server should receive piggybacked early data"
);
eprintln!(
"SUCCESS: Early data was piggybacked. Packets after Finished sent: {}",
packets_after_finished_sent
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls13_server_piggybacks_app_data_with_finished() {
use dimpl::certificate::generate_self_signed_certificate;
let _ = env_logger::try_init();
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = dtls13_config();
let mut now = Instant::now();
let mut client = Dtls::new_13(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_13(config, server_cert, now);
server.set_active(false);
let mut client_connected = false;
let mut server_connected = false;
let mut client_received_early_data = false;
let mut server_finished_sent = false;
let mut packets_after_server_finished = 0;
server
.send_application_data(b"Server early piggybacked data!")
.expect("queue server early data");
eprintln!("Queued server early application data before handshake");
for round in 0..50 {
client.handle_timeout(now).expect("client timeout");
server.handle_timeout(now).expect("server timeout");
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
if !server_finished_sent && !server_out.packets.is_empty() && round > 0 {
if round >= 1 {
server_finished_sent = true;
eprintln!("Round {}: Server sent its Finished flight", round);
}
}
if server_finished_sent && !client_received_early_data {
packets_after_server_finished += server_out.packets.len();
}
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if !client_out.app_data.is_empty() {
client_received_early_data = true;
let received = String::from_utf8_lossy(&client_out.app_data[0]);
eprintln!(
"Round {}: Client received early data from server: '{}' (packets since server Finished: {})",
round, received, packets_after_server_finished
);
assert_eq!(
&client_out.app_data[0][..],
b"Server early piggybacked data!",
"Should receive the server's queued early data"
);
}
client_connected |= client_out.connected;
server_connected |= server_out.connected;
if client_connected && server_connected && client_received_early_data {
break;
}
now += Duration::from_millis(10);
}
assert!(client_connected, "Client should connect");
assert!(server_connected, "Server should connect");
assert!(
client_received_early_data,
"Client should receive piggybacked early data from server"
);
eprintln!(
"SUCCESS: Server early data was piggybacked. Packets after server Finished: {}",
packets_after_server_finished
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls13_caches_app_data_when_handshake_packet_lost() {
use dimpl::certificate::generate_self_signed_certificate;
let _ = env_logger::try_init();
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = dtls13_config_with_mtu(200);
let mut now = Instant::now();
let mut client = Dtls::new_13(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_13(config, server_cert, now);
server.set_active(false);
let mut client_connected = false;
let mut server_connected = false;
let mut client_received_app_data = false;
let mut dropped_packet_round = None;
let mut server_first_flight_sent = false;
server
.send_application_data(b"Cached then decrypted!")
.expect("queue server app data");
eprintln!("Queued server application data before handshake");
for round in 0..100 {
client.handle_timeout(now).expect("client timeout");
server.handle_timeout(now).expect("server timeout");
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
deliver_packets(&client_out.packets, &mut server);
if !server_first_flight_sent && server_out.packets.len() > 2 && round > 0 {
server_first_flight_sent = true;
let num_packets = server_out.packets.len();
let drop_idx = num_packets / 2;
dropped_packet_round = Some(round);
eprintln!(
"Round {}: DROPPING packet {} of {} (simulating Certificate loss)",
round, drop_idx, num_packets
);
for (i, p) in server_out.packets.iter().enumerate() {
if i != drop_idx {
let _ = client.handle_packet(p);
}
}
} else {
if !server_out.packets.is_empty() && dropped_packet_round.is_some() {
eprintln!(
"Round {}: Server sending {} packets (retransmission)",
round,
server_out.packets.len()
);
}
deliver_packets(&server_out.packets, &mut client);
}
if !client_out.app_data.is_empty() {
client_received_app_data = true;
let received = String::from_utf8_lossy(&client_out.app_data[0]);
eprintln!(
"Round {}: Client received app data: '{}' (dropped packet was in round {:?})",
round, received, dropped_packet_round
);
assert_eq!(
&client_out.app_data[0][..],
b"Cached then decrypted!",
"Should receive the server's cached app data"
);
}
client_connected |= client_out.connected;
server_connected |= server_out.connected;
if client_connected && server_connected && client_received_app_data {
break;
}
now += Duration::from_millis(100);
}
assert!(
dropped_packet_round.is_some(),
"Test should have dropped a packet"
);
assert!(
client_connected,
"Client should connect after retransmission"
);
assert!(server_connected, "Server should connect");
assert!(
client_received_app_data,
"Client should receive cached app data after handshake completes"
);
eprintln!(
"SUCCESS: App data was cached during handshake packet loss and decrypted after retransmission"
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls13_large_application_data() {
use dimpl::certificate::generate_self_signed_certificate;
let _ = env_logger::try_init();
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = dtls13_config();
let mut now = Instant::now();
let mut client = Dtls::new_13(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_13(config, server_cert, now);
server.set_active(false);
for _ in 0..30 {
client.handle_timeout(now).expect("client timeout");
server.handle_timeout(now).expect("server timeout");
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_out.connected && server_out.connected {
break;
}
now += Duration::from_millis(10);
}
let large_data: Vec<u8> = (0..5000).map(|i| (i % 256) as u8).collect();
client
.send_application_data(&large_data)
.expect("client send large data");
let mut big_buf = vec![0u8; 8192];
let mut server_received: Vec<u8> = Vec::new();
for _ in 0..20 {
let mut client_packets: Vec<Vec<u8>> = Vec::new();
loop {
match client.poll_output(&mut big_buf) {
Output::Packet(p) => client_packets.push(p.to_vec()),
Output::Timeout(_) => break,
_ => {}
}
}
deliver_packets(&client_packets, &mut server);
loop {
match server.poll_output(&mut big_buf) {
Output::ApplicationData(data) => {
server_received.extend_from_slice(data);
}
Output::Timeout(_) => break,
_ => {}
}
}
if !server_received.is_empty() {
break;
}
now += Duration::from_millis(10);
}
assert_eq!(
server_received.len(),
5000,
"Server should receive all 5000 bytes"
);
assert_eq!(
server_received, large_data,
"Server should receive the exact large payload"
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls13_data_after_key_update() {
use dimpl::Config;
use dimpl::certificate::generate_self_signed_certificate;
let _ = env_logger::try_init();
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = Arc::new(
Config::builder()
.aead_encryption_limit(5)
.build()
.expect("build config"),
);
let mut now = Instant::now();
let mut client = Dtls::new_13(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_13(config, server_cert, now);
server.set_active(false);
for _ in 0..30 {
client.handle_timeout(now).expect("client timeout");
server.handle_timeout(now).expect("server timeout");
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_out.connected && server_out.connected {
break;
}
now += Duration::from_millis(10);
}
let total_messages = 15;
let mut server_received: Vec<Vec<u8>> = Vec::new();
for i in 0..total_messages {
let msg = format!("Message {}", i);
client
.send_application_data(msg.as_bytes())
.expect("client send");
for _ in 0..10 {
client.handle_timeout(now).expect("client timeout");
server.handle_timeout(now).expect("server timeout");
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
for data in server_out.app_data {
server_received.push(data);
}
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
now += Duration::from_millis(10);
}
}
assert_eq!(
server_received.len(),
total_messages,
"Server should receive all {} messages (got {})",
total_messages,
server_received.len()
);
for (i, data) in server_received.iter().enumerate() {
let expected = format!("Message {}", i);
assert_eq!(data, expected.as_bytes(), "Message {} should match", i);
}
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls13_queue_overflow_tx() {
use dimpl::Config;
use dimpl::certificate::generate_self_signed_certificate;
let _ = env_logger::try_init();
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = Arc::new(
Config::builder()
.max_queue_tx(3)
.build()
.expect("build config"),
);
let mut now = Instant::now();
let mut client = Dtls::new_13(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_13(config, server_cert, now);
server.set_active(false);
for _ in 0..30 {
client.handle_timeout(now).expect("client timeout");
server.handle_timeout(now).expect("server timeout");
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_out.connected && server_out.connected {
break;
}
now += Duration::from_millis(10);
}
let big_msg = vec![0xAB; 1100];
let mut overflow_error = false;
for i in 0..50 {
match client.send_application_data(&big_msg) {
Ok(()) => {}
Err(e) => {
eprintln!("TX queue overflow at message {}: {}", i, e);
assert!(
matches!(e, dimpl::Error::TransmitQueueFull),
"Expected TransmitQueueFull, got: {}",
e
);
overflow_error = true;
break;
}
}
}
assert!(
overflow_error,
"Should have received TransmitQueueFull error"
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls13_queue_overflow_rx() {
use dimpl::Config;
use dimpl::certificate::generate_self_signed_certificate;
let _ = env_logger::try_init();
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = Arc::new(
Config::builder()
.max_queue_rx(5)
.build()
.expect("build config"),
);
let mut now = Instant::now();
let mut client = Dtls::new_13(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_13(config, server_cert, now);
server.set_active(false);
for _ in 0..30 {
client.handle_timeout(now).expect("client timeout");
server.handle_timeout(now).expect("server timeout");
let client_out = drain_outputs(&mut client);
let server_out = drain_outputs(&mut server);
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_out.connected && server_out.connected {
break;
}
now += Duration::from_millis(10);
}
let mut all_packets: Vec<Vec<u8>> = Vec::new();
for i in 0..20 {
let msg = format!("Overflow msg {}", i);
client
.send_application_data(msg.as_bytes())
.expect("client send");
let client_out = drain_outputs(&mut client);
all_packets.extend(client_out.packets);
}
let mut overflow_error = false;
for (i, packet) in all_packets.iter().enumerate() {
match server.handle_packet(packet) {
Ok(()) => {}
Err(e) => {
eprintln!("RX queue overflow at packet {}: {}", i, e);
assert!(
matches!(e, dimpl::Error::ReceiveQueueFull),
"Expected ReceiveQueueFull, got: {}",
e
);
overflow_error = true;
break;
}
}
}
assert!(
overflow_error,
"Should have received ReceiveQueueFull error"
);
}