/* * Copyright 2017 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "pc/dtls_srtp_transport.h" #include #include #include #include #include "absl/functional/any_invocable.h" #include "api/dtls_transport_interface.h" #include "api/field_trials_view.h" #include "p2p/base/packet_transport_internal.h" #include "p2p/dtls/dtls_transport_internal.h" #include "pc/srtp_transport.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/ssl_stream_adapter.h" namespace webrtc { namespace { void ValidateAndLogTransport(DtlsTransportInternal* rtp_dtls_transport, DtlsTransportInternal* old_rtcp_dtls_transport, DtlsTransportInternal* rtcp_dtls_transport, bool is_srtp_active) { if (rtcp_dtls_transport && rtcp_dtls_transport != old_rtcp_dtls_transport) { // This would only be possible if using BUNDLE but not rtcp-mux, which isn't // allowed according to the BUNDLE spec. RTC_CHECK(!is_srtp_active) << "Setting RTCP for DTLS/SRTP after the DTLS is active " "should never happen."; } if (rtcp_dtls_transport && rtp_dtls_transport) { RTC_DCHECK_EQ(rtcp_dtls_transport->transport_name(), rtp_dtls_transport->transport_name()); } if (rtcp_dtls_transport) { RTC_LOG(LS_INFO) << "Setting RTCP Transport on " << rtcp_dtls_transport->transport_name() << " transport " << rtcp_dtls_transport; } if (rtp_dtls_transport) { RTC_LOG(LS_INFO) << "Setting RTP Transport on " << rtp_dtls_transport->transport_name() << " transport " << rtp_dtls_transport; } } } // namespace DtlsSrtpTransport::DtlsSrtpTransport(bool rtcp_mux_enabled, const FieldTrialsView& field_trials) : SrtpTransport(rtcp_mux_enabled, field_trials) {} void DtlsSrtpTransport::SetDtlsTransports(DtlsTransportInternal* rtp_dtls, DtlsTransportInternal* rtcp_dtls) { ValidateAndLogTransport(rtp_dtls, rtcp_dtls_transport(), rtcp_dtls, IsSrtpActive()); bool rtp_changed = MaybeUnsubscribe(rtp_dtls_transport(), rtp_dtls); bool rtcp_changed = MaybeUnsubscribe(rtcp_dtls_transport(), rtcp_dtls); // Now pass the RTP transport to RtpTransport. SetRtpPacketTransport(rtp_dtls); SetRtcpPacketTransport(rtcp_dtls); if (rtp_changed) { SetupDtlsTransport(rtp_dtls, /*is_rtcp=*/false); } if (rtcp_changed) { SetupDtlsTransport(rtcp_dtls, /*is_rtcp=*/true); } } void DtlsSrtpTransport::SetDtlsTransportsOwned( std::unique_ptr rtp_dtls, std::unique_ptr rtcp_dtls) { ValidateAndLogTransport(rtp_dtls.get(), rtcp_dtls_transport(), rtcp_dtls.get(), IsSrtpActive()); bool rtp_changed = MaybeUnsubscribe(rtp_dtls_transport(), rtp_dtls.get()); bool rtcp_changed = MaybeUnsubscribe(rtcp_dtls_transport(), rtcp_dtls.get()); // Pass the RTP transport to RtpTransport and ownership of // rtcp_dtls_transport. SetRtpPacketTransportOwned(std::move(rtp_dtls)); SetRtcpPacketTransportOwned(std::move(rtcp_dtls)); if (rtp_changed) { SetupDtlsTransport(rtp_dtls_transport(), /*is_rtcp=*/false); } if (rtcp_changed) { SetupDtlsTransport(rtcp_dtls_transport(), /*is_rtcp=*/true); } } void DtlsSrtpTransport::SetRtcpMuxEnabled(bool enable) { SrtpTransport::SetRtcpMuxEnabled(enable); if (enable) { MaybeSetupDtlsSrtp(); } } void DtlsSrtpTransport::UpdateSendEncryptedHeaderExtensionIds( const std::vector& send_extension_ids) { if (send_extension_ids_ == send_extension_ids) { return; } send_extension_ids_.emplace(send_extension_ids); if (DtlsHandshakeCompleted()) { // Reset the crypto parameters to update the send extension IDs. SetupRtpDtlsSrtp(); } } void DtlsSrtpTransport::UpdateRecvEncryptedHeaderExtensionIds( const std::vector& recv_extension_ids) { if (recv_extension_ids_ == recv_extension_ids) { return; } recv_extension_ids_.emplace(recv_extension_ids); if (DtlsHandshakeCompleted()) { // Reset the crypto parameters to update the receive extension IDs. SetupRtpDtlsSrtp(); } } bool DtlsSrtpTransport::IsDtlsActive() { auto rtcp_dtls_transport_ptr = rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport(); return rtp_dtls_transport() && rtp_dtls_transport()->IsDtlsActive() && (!rtcp_dtls_transport_ptr || rtcp_dtls_transport_ptr->IsDtlsActive()); } bool DtlsSrtpTransport::IsDtlsConnected() { auto rtcp_dtls_transport_ptr = rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport(); return rtp_dtls_transport() && rtp_dtls_transport()->dtls_state() == DtlsTransportState::kConnected && (!rtcp_dtls_transport_ptr || rtcp_dtls_transport_ptr->dtls_state() == DtlsTransportState::kConnected); } bool DtlsSrtpTransport::IsDtlsWritable() { auto rtcp_packet_transport = rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport(); return rtp_dtls_transport() && rtp_dtls_transport()->writable() && (!rtcp_packet_transport || rtcp_packet_transport->writable()); } bool DtlsSrtpTransport::DtlsHandshakeCompleted() { return IsDtlsActive() && IsDtlsConnected(); } void DtlsSrtpTransport::MaybeSetupDtlsSrtp() { if (IsSrtpActive() || !IsDtlsWritable()) { return; } SetupRtpDtlsSrtp(); if (!rtcp_mux_enabled() && rtcp_dtls_transport()) { SetupRtcpDtlsSrtp(); } } void DtlsSrtpTransport::SetupRtpDtlsSrtp() { // Use an empty encrypted header extension ID vector if not set. This could // happen when the DTLS handshake is completed before processing the // Offer/Answer which contains the encrypted header extension IDs. std::vector send_extension_ids; std::vector recv_extension_ids; if (send_extension_ids_) { send_extension_ids = *send_extension_ids_; } if (recv_extension_ids_) { recv_extension_ids = *recv_extension_ids_; } int selected_crypto_suite; ZeroOnFreeBuffer send_key; ZeroOnFreeBuffer recv_key; if (!ExtractParams(rtp_dtls_transport(), &selected_crypto_suite, &send_key, &recv_key) || !SetRtpParams(selected_crypto_suite, send_key, send_extension_ids, selected_crypto_suite, recv_key, recv_extension_ids)) { RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTP failed"; } } void DtlsSrtpTransport::SetupRtcpDtlsSrtp() { // Return if the DTLS-SRTP is active because the encrypted header extension // IDs don't need to be updated for RTCP and the crypto params don't need to // be reset. if (IsSrtpActive()) { return; } std::vector send_extension_ids; std::vector recv_extension_ids; if (send_extension_ids_) { send_extension_ids = *send_extension_ids_; } if (recv_extension_ids_) { recv_extension_ids = *recv_extension_ids_; } int selected_crypto_suite; ZeroOnFreeBuffer rtcp_send_key; ZeroOnFreeBuffer rtcp_recv_key; if (!ExtractParams(rtcp_dtls_transport(), &selected_crypto_suite, &rtcp_send_key, &rtcp_recv_key) || !SetRtcpParams(selected_crypto_suite, rtcp_send_key, send_extension_ids, selected_crypto_suite, rtcp_recv_key, recv_extension_ids)) { RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTCP failed"; } } bool DtlsSrtpTransport::ExtractParams(DtlsTransportInternal* dtls_transport, int* selected_crypto_suite, ZeroOnFreeBuffer* send_key, ZeroOnFreeBuffer* recv_key) { if (!dtls_transport || !dtls_transport->IsDtlsActive()) { return false; } if (!dtls_transport->GetSrtpCryptoSuite(selected_crypto_suite)) { RTC_LOG(LS_ERROR) << "No DTLS-SRTP selected crypto suite"; return false; } RTC_LOG(LS_INFO) << "Extracting keys from transport: " << dtls_transport->transport_name(); int key_len; int salt_len; if (!GetSrtpKeyAndSaltLengths((*selected_crypto_suite), &key_len, &salt_len)) { RTC_LOG(LS_ERROR) << "Unknown DTLS-SRTP crypto suite" << selected_crypto_suite; return false; } // OK, we're now doing DTLS (RFC 5764) ZeroOnFreeBuffer dtls_buffer(key_len * 2 + salt_len * 2); // RFC 5705 exporter using the RFC 5764 parameters if (!dtls_transport->ExportSrtpKeyingMaterial(dtls_buffer)) { RTC_LOG(LS_ERROR) << "DTLS-SRTP key export failed"; RTC_DCHECK_NOTREACHED(); // This should never happen return false; } // Sync up the keys with the DTLS-SRTP interface // https://datatracker.ietf.org/doc/html/rfc5764#section-4.2 // The keying material is in the format: // client_write_key|server_write_key|client_write_salt|server_write_salt ZeroOnFreeBuffer client_write_key(&dtls_buffer[0], key_len, key_len + salt_len); ZeroOnFreeBuffer server_write_key(&dtls_buffer[key_len], key_len, key_len + salt_len); client_write_key.AppendData(&dtls_buffer[key_len + key_len], salt_len); server_write_key.AppendData(&dtls_buffer[key_len + key_len + salt_len], salt_len); SSLRole role; if (!dtls_transport->GetDtlsRole(&role)) { RTC_LOG(LS_WARNING) << "Failed to get the DTLS role."; return false; } if (role == SSL_SERVER) { *send_key = std::move(server_write_key); *recv_key = std::move(client_write_key); } else { *send_key = std::move(client_write_key); *recv_key = std::move(server_write_key); } return true; } void DtlsSrtpTransport::SetupDtlsTransport( DtlsTransportInternal* dtls_transport, bool is_rtcp) { if (dtls_transport) { dtls_transport->SubscribeDtlsTransportState( this, [this](DtlsTransportInternal* transport, DtlsTransportState state) { OnDtlsState(transport, state); }); // Set the initial state. OnDtlsState(dtls_transport, dtls_transport->dtls_state()); } else { // When the transport is removed, we usually reset the SRTP parameters. // However, if the RTCP transport is removed because we are enabling RTCP // muxing, we should not reset the parameters because the SRTP session // will be maintained by the RTP transport. if (is_rtcp && rtcp_mux_enabled()) { return; } OnDtlsState(nullptr, DtlsTransportState::kNew); } } bool DtlsSrtpTransport::MaybeUnsubscribe(DtlsTransportInternal* old_transport, DtlsTransportInternal* new_transport) { if (old_transport && old_transport != new_transport) { old_transport->UnsubscribeDtlsTransportState(this); } return old_transport != new_transport; } void DtlsSrtpTransport::OnDtlsState(DtlsTransportInternal* transport, DtlsTransportState state) { RTC_DCHECK(transport == rtp_dtls_transport() || transport == rtcp_dtls_transport()); if (on_dtls_state_change_) { on_dtls_state_change_(); } if (state != DtlsTransportState::kConnected) { ResetParams(); } MaybeSetupDtlsSrtp(); } void DtlsSrtpTransport::OnWritableState( PacketTransportInternal* packet_transport) { MaybeSetupDtlsSrtp(); } void DtlsSrtpTransport::SetOnDtlsStateChange( absl::AnyInvocable callback) { on_dtls_state_change_ = std::move(callback); } } // namespace webrtc