Description
Hello everyone !
For a project, I wanted to integrate OpenAI's Realtime API using the WebRTC method.
However, upon connecting using this webrtc-rs library, I encountered a panic:
Panic in webrtc-srtp-0.14.0/src/key_derivation.rs
[2025-01-23T08:38:38Z INFO webrtc::peer_connection] ICE connection state changed: connected
Connection State has changed connected
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 0: Preparing
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 0: Sending
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 0: Waiting
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Recv [handshake:server] -> ClientHello (epoch: 0, seq: 0)
[2025-01-23T08:38:38Z DEBUG webrtc_dtls::flight::flight0] [handshake:server] use cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 0 -> Flight 2
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 2: Preparing
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 2: Sending
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Send [handshake:server] -> HelloVerifyRequest (epoch: 0, seq: 0)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 2: Waiting
[2025-01-23T08:38:38Z TRACE webrtc_ice::agent::agent_selector] inbound STUN (SuccessResponse) from udp4 host 40.84.168.209:3478 to udp4 host 192.168.1.52:52612
[2025-01-23T08:38:38Z TRACE webrtc_ice::agent::agent_selector] Found valid candidate pair: prio 9151314440652587007 (local, prio 2130706431) udp4 host 192.168.1.52:52612 <-> udp4 host 40.84.168.209:3478 (remote, prio 2130706431), p.state: 4, isUseCandidate: true, false
[2025-01-23T08:38:38Z TRACE webrtc_ice::agent::agent_internal] [controlling]: inbound STUN (Request) from 40.84.168.209:3478 to udp4 host 192.168.1.52:52612
[2025-01-23T08:38:38Z TRACE webrtc_ice::agent::agent_selector] controllingSelector: sendBindingSuccess
[2025-01-23T08:38:38Z TRACE webrtc_ice::agent::agent_selector] controllingSelector: after findPair prio 9151314440652587007 (local, prio 2130706431) udp4 host 192.168.1.52:52612 <-> udp4 host 40.84.168.209:3478 (remote, prio 2130706431), p.state: 4, false
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Recv [handshake:server] -> ClientHello (epoch: 0, seq: 1)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 2 -> Flight 4
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 4: Preparing
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 4: Sending
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Send [handshake:server] -> ServerHello (epoch: 0, seq: 1)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Send [handshake:server] -> Certificate (epoch: 0, seq: 2)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Send [handshake:server] -> ServerKeyExchange (epoch: 0, seq: 3)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Send [handshake:server] -> CertificateRequest (epoch: 0, seq: 4)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Send [handshake:server] -> ServerHelloDone (epoch: 0, seq: 5)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 4: Waiting
[2025-01-23T08:38:38Z TRACE webrtc_ice::agent::agent_selector] [controlling]: checking keepalive
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Recv [handshake:server] -> Certificate (epoch: 0, seq: 2)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Recv [handshake:server] -> ClientKeyExchange (epoch: 0, seq: 3)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Recv [handshake:server] -> CertificateVerify (epoch: 0, seq: 4)
[2025-01-23T08:38:38Z DEBUG webrtc_dtls::conn] server: CipherSuite not initialized, queuing packet
[2025-01-23T08:38:38Z DEBUG webrtc_dtls::conn] server: received packet of next epoch, queuing packet
[2025-01-23T08:38:38Z TRACE webrtc_dtls::flight::flight4] [handshake] PeerCertificates4 1
[2025-01-23T08:38:38Z TRACE webrtc_dtls::crypto] Picked an algorithm ECDSA_P256_SHA256_ASN1
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] server: <- ChangeCipherSpec (epoch: 1)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Recv [handshake:server] -> Finished (epoch: 1, seq: 5)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::flight::flight4] server peer_certificates.len() 1
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 4 -> Flight 6
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 6: Preparing
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] -> changeCipherSpec (epoch: 1)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 6: Sending
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Send [handshake:server] -> Finished (epoch: 1, seq: 6)
[2025-01-23T08:38:38Z TRACE webrtc_dtls::handshaker] [handshake:server] Flight 6: Finished
[2025-01-23T08:38:38Z TRACE webrtc_dtls::conn] Handshake Completed
thread 'tokio-runtime-worker' panicked at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/generic-array-0.14.7/src/lib.rs:572:9:
assertion `left == right` failed
left: 32
right: 16
stack backtrace:
0: rust_begin_unwind
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:665:5
1: core::panicking::panic_fmt
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/panicking.rs:74:14
2: core::panicking::assert_failed_inner
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/panicking.rs:410:17
3: core::panicking::assert_failed
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/panicking.rs:365:5
4: <&generic_array::GenericArray<T,N> as core::convert::From<&[T]>>::from
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/generic-array-0.14.7/src/lib.rs:572:9
5: <T as core::convert::Into<U>>::into
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/convert/mod.rs:759:9
6: generic_array::GenericArray<T,N>::from_slice
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/generic-array-0.14.7/src/lib.rs:550:15
7: webrtc_srtp::key_derivation::aes_cm_key_derivation
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webrtc-srtp-0.14.0/src/key_derivation.rs:43:15
8: webrtc_srtp::cipher::cipher_aead_aes_gcm::CipherAeadAesGcm::new
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webrtc-srtp-0.14.0/src/cipher/cipher_aead_aes_gcm.rs:154:32
9: webrtc_srtp::context::Context::new
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webrtc-srtp-0.14.0/src/context/mod.rs:127:26
10: webrtc_srtp::session::Session::new::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webrtc-srtp-0.14.0/src/session/mod.rs:44:29
11: webrtc::dtls_transport::RTCDtlsTransport::start_srtp::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webrtc-0.12.0/src/dtls_transport/mod.rs:217:26
12: webrtc::dtls_transport::RTCDtlsTransport::start::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webrtc-0.12.0/src/dtls_transport/mod.rs:475:27
13: webrtc::peer_connection::peer_connection_internal::PeerConnectionInternal::start_transports::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webrtc-0.12.0/src/peer_connection/peer_connection_internal.rs:708:14
14: webrtc::peer_connection::RTCPeerConnection::set_remote_description::{{closure}}::{{closure}}::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webrtc-0.12.0/src/peer_connection/mod.rs:1610:34
15: <core::pin::Pin<P> as core::future::future::Future>::poll
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/future/future.rs:123:9
16: webrtc::peer_connection::operation::Operations::start::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webrtc-0.12.0/src/peer_connection/operation/mod.rs:124:34
17: webrtc::peer_connection::operation::Operations::new::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webrtc-0.12.0/src/peer_connection/operation/mod.rs:57:60
18: tokio::runtime::task::core::Core<T,S>::poll::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/core.rs:331:17
19: tokio::loom::std::unsafe_cell::UnsafeCell<T>::with_mut
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/loom/std/unsafe_cell.rs:16:9
20: tokio::runtime::task::core::Core<T,S>::poll
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/core.rs:320:30
21: tokio::runtime::task::harness::poll_future::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/harness.rs:532:19
22: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/panic/unwind_safe.rs:272:9
23: std::panicking::try::do_call
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:557:40
24: std::panicking::try
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:521:19
25: std::panic::catch_unwind
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panic.rs:350:14
26: tokio::runtime::task::harness::poll_future
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/harness.rs:520:18
27: tokio::runtime::task::harness::Harness<T,S>::poll_inner
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/harness.rs:209:27
28: tokio::runtime::task::harness::Harness<T,S>::poll
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/harness.rs:154:15
29: tokio::runtime::task::raw::RawTask::poll
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/raw.rs:201:18
30: tokio::runtime::task::LocalNotified<S>::run
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/mod.rs:449:9
31: tokio::runtime::scheduler::multi_thread::worker::Context::run_task::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/scheduler/multi_thread/worker.rs:659:22
32: tokio::runtime::coop::with_budget
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/coop.rs:107:5
33: tokio::runtime::coop::budget
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/coop.rs:73:5
34: tokio::runtime::scheduler::multi_thread::worker::Context::run_task
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/scheduler/multi_thread/worker.rs:595:9
35: tokio::runtime::scheduler::multi_thread::worker::Context::run
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/scheduler/multi_thread/worker.rs:546:24
36: tokio::runtime::scheduler::multi_thread::worker::run::{{closure}}::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/scheduler/multi_thread/worker.rs:511:21
37: tokio::runtime::context::scoped::Scoped<T>::set
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/context/scoped.rs:40:9
38: tokio::runtime::context::set_scheduler::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/context.rs:180:26
39: std::thread::local::LocalKey<T>::try_with
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/thread/local.rs:283:12
40: std::thread::local::LocalKey<T>::with
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/thread/local.rs:260:9
41: tokio::runtime::context::set_scheduler
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/context.rs:180:17
42: tokio::runtime::scheduler::multi_thread::worker::run::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/scheduler/multi_thread/worker.rs:506:9
43: tokio::runtime::context::runtime::enter_runtime
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/context/runtime.rs:65:16
44: tokio::runtime::scheduler::multi_thread::worker::run
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/scheduler/multi_thread/worker.rs:498:5
45: tokio::runtime::scheduler::multi_thread::worker::Launch::launch::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/scheduler/multi_thread/worker.rs:464:45
46: <tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/blocking/task.rs:42:21
47: tokio::runtime::task::core::Core<T,S>::poll::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/core.rs:331:17
48: tokio::loom::std::unsafe_cell::UnsafeCell<T>::with_mut
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/loom/std/unsafe_cell.rs:16:9
49: tokio::runtime::task::core::Core<T,S>::poll
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/core.rs:320:30
50: tokio::runtime::task::harness::poll_future::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/harness.rs:532:19
51: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/panic/unwind_safe.rs:272:9
52: std::panicking::try::do_call
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:557:40
53: std::panicking::try
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:521:19
54: std::panic::catch_unwind
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panic.rs:350:14
55: tokio::runtime::task::harness::poll_future
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/harness.rs:520:18
56: tokio::runtime::task::harness::Harness<T,S>::poll_inner
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/harness.rs:209:27
57: tokio::runtime::task::harness::Harness<T,S>::poll
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/harness.rs:154:15
58: tokio::runtime::task::raw::RawTask::poll
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/raw.rs:201:18
59: tokio::runtime::task::UnownedTask<S>::run
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/task/mod.rs:486:9
60: tokio::runtime::blocking::pool::Task::run
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/blocking/pool.rs:161:9
61: tokio::runtime::blocking::pool::Inner::run
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/blocking/pool.rs:511:17
62: tokio::runtime::blocking::pool::Spawner::spawn_thread::{{closure}}
at /home/deluvi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.43.0/src/runtime/blocking/pool.rs:469:13
After digging, I found out that the panic is due to trying to fit a 32 bytes slice into a 16 bytes AES128 key.
At this point, I decided to debug the issue by patching the library to add debug prints. Here is what I found.
First, the cipher picked:
[/home/deluvi/work/webrtc/webrtc/src/dtls_transport/mod.rs:201:17] cipher_suite.as_ref().unwrap().to_string() = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
So AES128, which makes sense that we expected a 16 bytes key. But why did the library generated a 32 bytes key ?
I found out that the key len is calculated in extract_session_keys_from_dtls
. By adding further debug prints, I found out that:
[/home/deluvi/work/webrtc/srtp/src/config.rs:46:23] self.profile.key_len() = 32
[/home/deluvi/work/webrtc/srtp/src/config.rs:47:9] &self.profile = AeadAes256Gcm
Aes256, what?!
After digging a bit more, I found out that this profile can be set through the SRTP protection profile extension:
[/home/deluvi/work/webrtc/webrtc/src/dtls_transport/mod.rs:417:28] dtls_conn.selected_srtpprotection_profile() = Srtp_Aead_Aes_256_Gcm
I found out that this was picked in flight 0:
[/home/deluvi/work/webrtc/dtls/src/flight/flight0.rs:118:29] &e.protection_profiles = [
Srtp_Aead_Aes_256_Gcm,
Srtp_Aead_Aes_128_Gcm,
Srtp_Aes128_Cm_Hmac_Sha1_80,
]
This correspond to the use_srtp
extension that the server gives if I understood correctly.
This list is given to find_matching_srtp_profile
, which pick the first compatible srtp profile. The Aes256 one being the first, it is the one that got picked by the client.
We take the key length of the srtp profile (Aes256, 32 bytes), which does not match the key length of the overall cipher (Aes128, 16 bytes), which cause the issue.
By patching the crate and overriding the dtls_conn.selected_srtpprotection_profile()
to Srtp_Aead_Aes_128_Gcm
, I managed to successfully communicate with OpenAI backend, so this was indeed a bad handling of the use_srtp
extension by the crate.