use std::sync::Arc;
use std::time::{Duration, Instant};
use dimpl::{Config, Dtls, SrtpProfile};
use crate::common::*;
#[test]
#[cfg(feature = "rcgen")]
fn dtls12_cookie_retry_proceeds_to_server_hello() {
use dimpl::certificate::generate_self_signed_certificate;
let now = Instant::now();
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().build().expect("Failed to build config"));
let mut client = Dtls::new_12(config.clone(), client_cert.clone(), now);
client.set_active(true);
let mut server = Dtls::new_12(config.clone(), server_cert.clone(), now);
server.set_active(false);
client.handle_timeout(now).expect("client timeout start");
client.handle_timeout(now).expect("client arm flight 1");
let f1 = collect_packets(&mut client);
assert!(!f1.is_empty(), "client should emit ClientHello");
let f1_hs_types: Vec<u8> = f1.iter().flat_map(|p| parse_handshake_types(p)).collect();
assert!(
f1_hs_types.contains(&CLIENT_HELLO),
"flight 1 should contain ClientHello, got {:?}",
f1_hs_types
);
for p in &f1 {
server.handle_packet(p).expect("server recv f1");
}
server.handle_timeout(now).expect("server arm flight 2");
let f2 = collect_packets(&mut server);
assert!(!f2.is_empty(), "server should emit HelloVerifyRequest");
let f2_hs_types: Vec<u8> = f2.iter().flat_map(|p| parse_handshake_types(p)).collect();
assert!(
f2_hs_types.contains(&HELLO_VERIFY_REQUEST),
"flight 2 should contain HelloVerifyRequest, got {:?}",
f2_hs_types
);
for p in &f2 {
client.handle_packet(p).expect("client recv f2");
}
client.handle_timeout(now).expect("client arm flight 3");
let f3 = collect_packets(&mut client);
assert!(!f3.is_empty(), "client should emit ClientHello with cookie");
let f3_hs_types: Vec<u8> = f3.iter().flat_map(|p| parse_handshake_types(p)).collect();
assert!(
f3_hs_types.contains(&CLIENT_HELLO),
"flight 3 should contain ClientHello (with cookie), got {:?}",
f3_hs_types
);
for p in &f3 {
server.handle_packet(p).expect("server recv f3");
}
server.handle_timeout(now).expect("server arm flight 4");
let f4 = collect_packets(&mut server);
assert!(
!f4.is_empty(),
"server should emit flight 4 after ClientHello with cookie"
);
let f4_hs_types: Vec<u8> = f4.iter().flat_map(|p| parse_handshake_types(p)).collect();
assert!(
!f4_hs_types.contains(&HELLO_VERIFY_REQUEST),
"server should NOT send HelloVerifyRequest after valid cookie - BUG! got {:?}",
f4_hs_types
);
assert!(
f4_hs_types.contains(&SERVER_HELLO),
"server should send ServerHello after valid cookie, got {:?}",
f4_hs_types
);
assert!(
f4_hs_types.contains(&CERTIFICATE),
"server should send Certificate, got {:?}",
f4_hs_types
);
assert!(
f4_hs_types.contains(&SERVER_HELLO_DONE),
"server should send ServerHelloDone, got {:?}",
f4_hs_types
);
println!(
"SUCCESS: Server correctly processed ClientHello with cookie and sent ServerHello flight"
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls12_parallel_handshakes_with_cookies() {
use dimpl::certificate::generate_self_signed_certificate;
let now = Instant::now();
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().build().expect("Failed to build config"));
let mut pairs: Vec<(Dtls, Dtls)> = (0..5)
.map(|_| {
let mut client = Dtls::new_12(config.clone(), client_cert.clone(), now);
client.set_active(true);
let mut server = Dtls::new_12(config.clone(), server_cert.clone(), now);
server.set_active(false);
(client, server)
})
.collect();
for (i, (client, server)) in pairs.iter_mut().enumerate() {
client.handle_timeout(now).expect("client timeout");
client.handle_timeout(now).expect("client arm f1");
let f1 = collect_packets(client);
for p in &f1 {
server.handle_packet(p).expect("server recv f1");
}
server.handle_timeout(now).expect("server arm f2");
let f2 = collect_packets(server);
for p in &f2 {
client.handle_packet(p).expect("client recv f2");
}
client.handle_timeout(now).expect("client arm f3");
let f3 = collect_packets(client);
for p in &f3 {
server.handle_packet(p).expect("server recv f3");
}
server.handle_timeout(now).expect("server arm f4");
let f4 = collect_packets(server);
let f4_hs_types: Vec<u8> = f4.iter().flat_map(|p| parse_handshake_types(p)).collect();
assert!(
!f4_hs_types.contains(&HELLO_VERIFY_REQUEST),
"pair {}: server sent HelloVerifyRequest instead of ServerHello - BUG!",
i
);
assert!(
f4_hs_types.contains(&SERVER_HELLO),
"pair {}: server should send ServerHello, got {:?}",
i,
f4_hs_types
);
}
println!(
"SUCCESS: All {} parallel handshakes processed cookies correctly",
pairs.len()
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls12_retransmit_no_cookie_after_cookie_sent() {
use dimpl::certificate::generate_self_signed_certificate;
let now = Instant::now();
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().build().expect("Failed to build config"));
let mut client = Dtls::new_12(config.clone(), client_cert.clone(), now);
client.set_active(true);
let mut server = Dtls::new_12(config.clone(), server_cert.clone(), now);
server.set_active(false);
client.handle_timeout(now).expect("client timeout");
client.handle_timeout(now).expect("client arm f1");
let f1 = collect_packets(&mut client);
assert!(!f1.is_empty());
let f1_copy = f1.clone();
for p in &f1 {
server.handle_packet(p).expect("server recv f1");
}
server.handle_timeout(now).expect("server arm f2");
let f2 = collect_packets(&mut server);
assert!(!f2.is_empty());
for p in &f2 {
client.handle_packet(p).expect("client recv f2");
}
client.handle_timeout(now).expect("client arm f3");
let f3 = collect_packets(&mut client);
assert!(!f3.is_empty());
for p in &f3 {
server.handle_packet(p).expect("server recv f3 with cookie");
}
for p in &f1_copy {
server
.handle_packet(p)
.expect("server recv retransmit of no-cookie CH");
}
server.handle_timeout(now).expect("server arm f4");
let f4 = collect_packets(&mut server);
assert!(!f4.is_empty(), "server should emit flight 4");
let f4_hs_types: Vec<u8> = f4.iter().flat_map(|p| parse_handshake_types(p)).collect();
assert!(
f4_hs_types.contains(&SERVER_HELLO),
"server should send ServerHello even after retransmit of no-cookie CH, got {:?}",
f4_hs_types
);
println!("SUCCESS: Server correctly handled out-of-order retransmit scenario");
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls12_retransmit_no_cookie_before_cookie_received() {
use dimpl::certificate::generate_self_signed_certificate;
let now = Instant::now();
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().build().expect("Failed to build config"));
let mut client = Dtls::new_12(config.clone(), client_cert.clone(), now);
client.set_active(true);
let mut server = Dtls::new_12(config.clone(), server_cert.clone(), now);
server.set_active(false);
client.handle_timeout(now).expect("client timeout");
client.handle_timeout(now).expect("client arm f1");
let f1 = collect_packets(&mut client);
assert!(!f1.is_empty());
let f1_copy = f1.clone();
for p in &f1 {
server.handle_packet(p).expect("server recv f1");
}
server.handle_timeout(now).expect("server arm f2");
let f2 = collect_packets(&mut server);
assert!(!f2.is_empty());
for p in &f1_copy {
server
.handle_packet(p)
.expect("server recv retransmit of no-cookie CH");
}
let f2_resend = collect_packets(&mut server);
let f2_resend_types: Vec<u8> = f2_resend
.iter()
.flat_map(|p| parse_handshake_types(p))
.collect();
assert!(
f2_resend_types.contains(&HELLO_VERIFY_REQUEST),
"server should resend HelloVerifyRequest after duplicate, got {:?}",
f2_resend_types
);
for p in &f2 {
client.handle_packet(p).expect("client recv f2");
}
client.handle_timeout(now).expect("client arm f3");
let f3 = collect_packets(&mut client);
assert!(!f3.is_empty());
for p in &f3 {
server.handle_packet(p).expect("server recv f3 with cookie");
}
server.handle_timeout(now).expect("server arm f4");
let f4 = collect_packets(&mut server);
assert!(!f4.is_empty(), "server should emit flight 4");
let f4_hs_types: Vec<u8> = f4.iter().flat_map(|p| parse_handshake_types(p)).collect();
assert!(
!f4_hs_types.contains(&HELLO_VERIFY_REQUEST),
"server should NOT resend HelloVerifyRequest after valid cookie, got {:?}",
f4_hs_types
);
assert!(
f4_hs_types.contains(&SERVER_HELLO),
"server should send ServerHello after cookie CH, got {:?}",
f4_hs_types
);
println!("SUCCESS: Old duplicate ClientHello did not block new ClientHello with cookie");
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls12_basic_handshake() {
use dimpl::certificate::generate_self_signed_certificate;
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = dtls12_config();
let mut now = Instant::now();
let mut client = Dtls::new_12(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_12(config, server_cert, now);
server.set_active(false);
let mut client_connected = false;
let mut server_connected = 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);
if client_out.connected {
client_connected = true;
}
if server_out.connected {
server_connected = true;
}
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_connected && server_connected {
break;
}
now += Duration::from_millis(10);
}
assert!(client_connected, "Client should be connected");
assert!(server_connected, "Server should be connected");
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls12_handshake_with_keying_material() {
use dimpl::certificate::generate_self_signed_certificate;
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let config = dtls12_config();
let mut now = Instant::now();
let mut client = Dtls::new_12(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_12(config, server_cert, now);
server.set_active(false);
let mut client_km: Option<(Vec<u8>, SrtpProfile)> = None;
let mut server_km: Option<(Vec<u8>, SrtpProfile)> = None;
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);
if let Some(km) = client_out.keying_material {
client_km = Some(km);
}
if let Some(km) = server_out.keying_material {
server_km = Some(km);
}
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_km.is_some() && server_km.is_some() {
break;
}
now += Duration::from_millis(10);
}
let client_km = client_km.expect("Client should have keying material");
let server_km = server_km.expect("Server should have keying material");
assert_eq!(
client_km.0, server_km.0,
"Client and server keying material should match"
);
assert_eq!(
client_km.1, server_km.1,
"Client and server SRTP profile should match"
);
assert!(
!client_km.0.is_empty(),
"Keying material should not be empty"
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls12_peer_certificate_exchange() {
use dimpl::certificate::generate_self_signed_certificate;
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let expected_client_cert = client_cert.certificate.clone();
let expected_server_cert = server_cert.certificate.clone();
let config = dtls12_config();
let mut now = Instant::now();
let mut client = Dtls::new_12(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_12(config, server_cert, now);
server.set_active(false);
let mut client_peer_cert: Option<Vec<u8>> = None;
let mut server_peer_cert: Option<Vec<u8>> = None;
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);
if let Some(cert) = client_out.peer_cert {
client_peer_cert = Some(cert);
}
if let Some(cert) = server_out.peer_cert {
server_peer_cert = Some(cert);
}
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_peer_cert.is_some() && server_peer_cert.is_some() {
break;
}
now += Duration::from_millis(10);
}
assert!(
client_peer_cert.is_some(),
"Client should receive server's certificate"
);
assert!(
server_peer_cert.is_some(),
"Server should receive client's certificate"
);
assert_eq!(
client_peer_cert.unwrap(),
expected_server_cert,
"Client should receive server's certificate"
);
assert_eq!(
server_peer_cert.unwrap(),
expected_client_cert,
"Server should receive client's certificate"
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls12_handshake_client_certificate_auth() {
use dimpl::certificate::generate_self_signed_certificate;
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let expected_client_cert = client_cert.certificate.clone();
let config = Arc::new(
Config::builder()
.require_client_certificate(true)
.build()
.expect("Failed to build config"),
);
let mut now = Instant::now();
let mut client = Dtls::new_12(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_12(config, server_cert, now);
server.set_active(false);
let mut server_connected = false;
let mut server_peer_cert: Option<Vec<u8>> = None;
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);
if server_out.connected {
server_connected = true;
}
if let Some(cert) = server_out.peer_cert {
server_peer_cert = Some(cert);
}
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if server_connected && server_peer_cert.is_some() {
break;
}
now += Duration::from_millis(10);
}
assert!(
server_connected,
"Server should be connected after client certificate auth"
);
assert!(
server_peer_cert.is_some(),
"Server should receive client's certificate"
);
assert_eq!(
server_peer_cert.unwrap(),
expected_client_cert,
"Server should receive the correct client certificate"
);
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls12_handshake_secp384r1_key_exchange() {
use dimpl::certificate::generate_self_signed_certificate;
use dimpl::crypto::aws_lc_rs;
let client_cert = generate_self_signed_certificate().expect("gen client cert");
let server_cert = generate_self_signed_certificate().expect("gen server cert");
let mut provider = aws_lc_rs::default_provider();
let p384_vec: Vec<&'static dyn dimpl::crypto::SupportedKxGroup> = provider
.kx_groups
.iter()
.copied()
.filter(|g| g.name() == dimpl::NamedGroup::Secp384r1)
.collect();
let p384_only: &'static [&'static dyn dimpl::crypto::SupportedKxGroup] =
Box::leak(p384_vec.into_boxed_slice());
provider.kx_groups = p384_only;
let config = Arc::new(
Config::builder()
.with_crypto_provider(provider)
.build()
.expect("Failed to build config with P-384 only"),
);
let mut now = Instant::now();
let mut client = Dtls::new_12(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_12(config, server_cert, now);
server.set_active(false);
let mut client_connected = false;
let mut server_connected = 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);
if client_out.connected {
client_connected = true;
}
if server_out.connected {
server_connected = true;
}
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_connected && server_connected {
break;
}
now += Duration::from_millis(10);
}
assert!(client_connected, "Client should be connected with P-384");
assert!(server_connected, "Server should be connected with P-384");
}
#[test]
#[cfg(feature = "rcgen")]
fn dtls12_handshake_timeout_expires() {
use dimpl::certificate::generate_self_signed_certificate;
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()
.handshake_timeout(Duration::from_secs(5))
.flight_retries(2)
.build()
.expect("Failed to build config"),
);
let mut now = Instant::now();
let mut client = Dtls::new_12(Arc::clone(&config), client_cert, now);
client.set_active(true);
let mut server = Dtls::new_12(config, server_cert, now);
server.set_active(false);
let mut client_timed_out = false;
let mut server_timed_out = false;
for _ in 0..100 {
if !client_timed_out {
match client.handle_timeout(now) {
Ok(()) => {
let _ = drain_outputs(&mut client);
}
Err(_) => {
client_timed_out = true;
}
}
}
if !server_timed_out {
match server.handle_timeout(now) {
Ok(()) => {
let _ = drain_outputs(&mut server);
}
Err(_) => {
server_timed_out = true;
}
}
}
if client_timed_out && server_timed_out {
break;
}
now += Duration::from_secs(2);
}
assert!(
client_timed_out,
"Client handshake should eventually time out when no packets are delivered"
);
}
#[test]
fn dtls12_handshake_p384_certificate() {
use crate::ossl_helper::{DtlsCertOptions, DtlsPKeyType, OsslDtlsCert};
let client_cert = OsslDtlsCert::new(DtlsCertOptions {
common_name: "WebRTC".into(),
pkey_type: DtlsPKeyType::EcDsaP384,
});
let server_cert = OsslDtlsCert::new(DtlsCertOptions {
common_name: "WebRTC".into(),
pkey_type: DtlsPKeyType::EcDsaP384,
});
let config = dtls12_config();
let mut now = Instant::now();
let mut client = Dtls::new_12(
Arc::clone(&config),
dimpl::DtlsCertificate {
certificate: client_cert.x509.to_der().expect("client cert der"),
private_key: client_cert
.pkey
.private_key_to_der()
.expect("client key der"),
},
now,
);
client.set_active(true);
let mut server = Dtls::new_12(
config,
dimpl::DtlsCertificate {
certificate: server_cert.x509.to_der().expect("server cert der"),
private_key: server_cert
.pkey
.private_key_to_der()
.expect("server key der"),
},
now,
);
server.set_active(false);
let mut client_connected = false;
let mut server_connected = 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);
if client_out.connected {
client_connected = true;
}
if server_out.connected {
server_connected = true;
}
deliver_packets(&client_out.packets, &mut server);
deliver_packets(&server_out.packets, &mut client);
if client_connected && server_connected {
break;
}
now += Duration::from_millis(10);
}
assert!(client_connected, "Client should connect with P-384 cert");
assert!(server_connected, "Server should connect with P-384 cert");
}