Skip to content
File

Blob: firmware/vendor/str0m/tests/dtls-security.rs

rust198 lines
1//! Tests for DTLS handshake edge cases and security.
2 
3use std::net::Ipv4Addr;
4use std::time::{Duration, Instant};
5 
6use str0m::Rtc;
7use str0m::RtcError;
8 
9mod common;
10use common::{Peer, TestRtc, init_crypto_default, init_log, progress};
11 
12/// Test certificate fingerprint format and uniqueness.
13#[test]
14fn dtls_certificate_fingerprint_format() -> Result<(), RtcError> {
15 init_log();
16 init_crypto_default();
17 
18 // Create two instances and verify they have different certificates
19 let mut l = TestRtc::new(Peer::Left);
20 let mut r = TestRtc::new(Peer::Right);
21 
22 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
23 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
24 
25 // Get fingerprints
26 let finger_l = l.direct_api().local_dtls_fingerprint().clone();
27 let finger_r = r.direct_api().local_dtls_fingerprint().clone();
28 
29 // Fingerprints should be different for different instances
30 assert_ne!(
31 finger_l, finger_r,
32 "Different Rtc instances should have different certificates"
33 );
34 
35 // Fingerprint should be properly formatted (SHA-256 format)
36 let finger_str = format!("{}", finger_l);
37 assert!(
38 finger_str.starts_with("sha-256 ") || finger_str.contains(':'),
39 "Fingerprint should be in SHA-256 format: {}",
40 finger_str
41 );
42 
43 Ok(())
44}
45 
46/// Test that new Rtc instances get new certificates.
47#[test]
48fn dtls_certificate_rotation() -> Result<(), RtcError> {
49 init_log();
50 init_crypto_default();
51 
52 // Create first connection using L/R peers (respects L_CRYPTO/R_CRYPTO env vars)
53 let mut l1 = TestRtc::new(Peer::Left);
54 let mut r1 = TestRtc::new(Peer::Right);
55 
56 let finger1_l = l1.direct_api().local_dtls_fingerprint().clone();
57 let finger1_r = r1.direct_api().local_dtls_fingerprint().clone();
58 
59 // Create second connection with new certificates
60 let mut l2 = TestRtc::new(Peer::Left);
61 let mut r2 = TestRtc::new(Peer::Right);
62 
63 let finger2_l = l2.direct_api().local_dtls_fingerprint().clone();
64 let finger2_r = r2.direct_api().local_dtls_fingerprint().clone();
65 
66 // All fingerprints should be unique
67 assert_ne!(finger1_l, finger1_r, "L1 and R1 should differ");
68 assert_ne!(finger2_l, finger2_r, "L2 and R2 should differ");
69 assert_ne!(finger1_l, finger2_l, "L1 and L2 should differ");
70 assert_ne!(finger1_r, finger2_r, "R1 and R2 should differ");
71 
72 Ok(())
73}
74 
75/// Test DTLS connection with ice-lite mode (passive role).
76#[test]
77fn dtls_with_ice_lite() -> Result<(), RtcError> {
78 init_log();
79 init_crypto_default();
80 
81 let mut l = TestRtc::new(Peer::Left);
82 
83 // R is ice-lite (typically server-side), but still use Peer::Right crypto provider
84 let mut rtc_r_builder = Rtc::builder().set_ice_lite(true);
85 if let Some(crypto) = Peer::Right.crypto_provider() {
86 rtc_r_builder = rtc_r_builder.set_crypto_provider(crypto);
87 }
88 let mut r = TestRtc::new_with_rtc(Peer::Right.span(), rtc_r_builder.build(Instant::now()));
89 
90 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
91 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
92 
93 let (offer, pending) = l.span.in_scope(|| {
94 let mut change = l.rtc.sdp_api();
95 let _ = change.add_channel("test".into());
96 change.apply().unwrap()
97 });
98 
99 let answer = r.span.in_scope(|| r.rtc.sdp_api().accept_offer(offer))?;
100 l.span
101 .in_scope(|| l.rtc.sdp_api().accept_answer(pending, answer))?;
102 
103 loop {
104 if l.is_connected() && r.is_connected() {
105 break;
106 }
107 if l.duration() > Duration::from_secs(5) {
108 panic!("Failed to connect with ice-lite");
109 }
110 progress(&mut l, &mut r)?;
111 }
112 
113 Ok(())
114}
115 
116/// Test pregenerated DTLS certificate.
117#[test]
118fn dtls_pregenerated_certificate() -> Result<(), RtcError> {
119 init_log();
120 init_crypto_default();
121 
122 // Generate a certificate using Peer::Left's crypto provider if set
123 let provider = Peer::Left
124 .crypto_provider()
125 .unwrap_or_else(|| std::sync::Arc::new(str0m::crypto::from_feature_flags()));
126 let cert = provider.dtls_provider.generate_certificate().unwrap();
127 
128 // Use the pregenerated certificate with the same crypto provider
129 let mut rtc_l_builder = Rtc::builder().set_dtls_cert(cert);
130 if let Some(crypto) = Peer::Left.crypto_provider() {
131 rtc_l_builder = rtc_l_builder.set_crypto_provider(crypto);
132 }
133 
134 let mut l = TestRtc::new_with_rtc(Peer::Left.span(), rtc_l_builder.build(Instant::now()));
135 let mut r = TestRtc::new(Peer::Right);
136 
137 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
138 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
139 
140 let (offer, pending) = l.span.in_scope(|| {
141 let mut change = l.rtc.sdp_api();
142 let _ = change.add_channel("test".into());
143 change.apply().unwrap()
144 });
145 
146 let answer = r.span.in_scope(|| r.rtc.sdp_api().accept_offer(offer))?;
147 l.span
148 .in_scope(|| l.rtc.sdp_api().accept_answer(pending, answer))?;
149 
150 loop {
151 if l.is_connected() && r.is_connected() {
152 break;
153 }
154 if l.duration() > Duration::from_secs(5) {
155 panic!("Failed to connect with pregenerated certificate");
156 }
157 progress(&mut l, &mut r)?;
158 }
159 
160 Ok(())
161}
162 
163/// Test that same pregenerated certificate produces same fingerprint.
164#[test]
165fn dtls_pregenerated_certificate_same_fingerprint() -> Result<(), RtcError> {
166 init_log();
167 init_crypto_default();
168 
169 // Generate a certificate using Peer::Left's crypto provider if set
170 let provider = Peer::Left
171 .crypto_provider()
172 .unwrap_or_else(|| std::sync::Arc::new(str0m::crypto::from_feature_flags()));
173 let cert = provider.dtls_provider.generate_certificate().unwrap();
174 let cert_clone = cert.clone();
175 
176 // Create two instances with the same certificate
177 let mut rtc1_builder = Rtc::builder().set_dtls_cert(cert);
178 let mut rtc2_builder = Rtc::builder().set_dtls_cert(cert_clone);
179 if let Some(crypto) = Peer::Left.crypto_provider() {
180 rtc1_builder = rtc1_builder.set_crypto_provider(crypto.clone());
181 rtc2_builder = rtc2_builder.set_crypto_provider(crypto);
182 }
183 
184 let mut l1 = TestRtc::new_with_rtc(Peer::Left.span(), rtc1_builder.build(Instant::now()));
185 let mut l2 = TestRtc::new_with_rtc(Peer::Left.span(), rtc2_builder.build(Instant::now()));
186 
187 let finger1 = l1.direct_api().local_dtls_fingerprint().clone();
188 let finger2 = l2.direct_api().local_dtls_fingerprint().clone();
189 
190 // Same certificate should produce same fingerprint
191 assert_eq!(
192 finger1, finger2,
193 "Same certificate should produce same fingerprint"
194 );
195 
196 Ok(())
197}