diff options
Diffstat (limited to 'third_party/libwebrtc/pc/dtls_srtp_transport.cc')
-rw-r--r-- | third_party/libwebrtc/pc/dtls_srtp_transport.cc | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/third_party/libwebrtc/pc/dtls_srtp_transport.cc b/third_party/libwebrtc/pc/dtls_srtp_transport.cc new file mode 100644 index 0000000000..28de50b2ae --- /dev/null +++ b/third_party/libwebrtc/pc/dtls_srtp_transport.cc @@ -0,0 +1,328 @@ +/* + * 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 <string.h> + +#include <string> +#include <utility> + +#include "api/dtls_transport_interface.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/ssl_stream_adapter.h" + +namespace { +// Value specified in RFC 5764. +static const char kDtlsSrtpExporterLabel[] = "EXTRACTOR-dtls_srtp"; +} // namespace + +namespace webrtc { + +DtlsSrtpTransport::DtlsSrtpTransport(bool rtcp_mux_enabled, + const FieldTrialsView& field_trials) + : SrtpTransport(rtcp_mux_enabled, field_trials) {} + +void DtlsSrtpTransport::SetDtlsTransports( + cricket::DtlsTransportInternal* rtp_dtls_transport, + cricket::DtlsTransportInternal* rtcp_dtls_transport) { + // Transport names should be the same. + if (rtp_dtls_transport && rtcp_dtls_transport) { + RTC_DCHECK(rtp_dtls_transport->transport_name() == + rtcp_dtls_transport->transport_name()); + } + + // When using DTLS-SRTP, we must reset the SrtpTransport every time the + // DtlsTransport changes and wait until the DTLS handshake is complete to set + // the newly negotiated parameters. + // If `active_reset_srtp_params_` is true, intentionally reset the SRTP + // parameter even though the DtlsTransport may not change. + if (IsSrtpActive() && (rtp_dtls_transport != rtp_dtls_transport_ || + active_reset_srtp_params_)) { + ResetParams(); + } + + const std::string transport_name = + rtp_dtls_transport ? rtp_dtls_transport->transport_name() : "null"; + + if (rtcp_dtls_transport && rtcp_dtls_transport != 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(!(IsSrtpActive())) + << "Setting RTCP for DTLS/SRTP after the DTLS is active " + "should never happen."; + } + + RTC_LOG(LS_INFO) << "Setting RTCP Transport on " << transport_name + << " transport " << rtcp_dtls_transport; + SetRtcpDtlsTransport(rtcp_dtls_transport); + SetRtcpPacketTransport(rtcp_dtls_transport); + + RTC_LOG(LS_INFO) << "Setting RTP Transport on " << transport_name + << " transport " << rtp_dtls_transport; + SetRtpDtlsTransport(rtp_dtls_transport); + SetRtpPacketTransport(rtp_dtls_transport); + + MaybeSetupDtlsSrtp(); +} + +void DtlsSrtpTransport::SetRtcpMuxEnabled(bool enable) { + SrtpTransport::SetRtcpMuxEnabled(enable); + if (enable) { + MaybeSetupDtlsSrtp(); + } +} + +void DtlsSrtpTransport::UpdateSendEncryptedHeaderExtensionIds( + const std::vector<int>& 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<int>& 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 = + rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_; + return (rtp_dtls_transport_ && rtp_dtls_transport_->IsDtlsActive() && + (!rtcp_dtls_transport || rtcp_dtls_transport->IsDtlsActive())); +} + +bool DtlsSrtpTransport::IsDtlsConnected() { + auto rtcp_dtls_transport = + rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_; + return (rtp_dtls_transport_ && + rtp_dtls_transport_->dtls_state() == DtlsTransportState::kConnected && + (!rtcp_dtls_transport || rtcp_dtls_transport->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<int> send_extension_ids; + std::vector<int> 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; + rtc::ZeroOnFreeBuffer<unsigned char> send_key; + rtc::ZeroOnFreeBuffer<unsigned char> recv_key; + + if (!ExtractParams(rtp_dtls_transport_, &selected_crypto_suite, &send_key, + &recv_key) || + !SetRtpParams(selected_crypto_suite, &send_key[0], + static_cast<int>(send_key.size()), send_extension_ids, + selected_crypto_suite, &recv_key[0], + static_cast<int>(recv_key.size()), 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<int> send_extension_ids; + std::vector<int> 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; + rtc::ZeroOnFreeBuffer<unsigned char> rtcp_send_key; + rtc::ZeroOnFreeBuffer<unsigned char> rtcp_recv_key; + if (!ExtractParams(rtcp_dtls_transport_, &selected_crypto_suite, + &rtcp_send_key, &rtcp_recv_key) || + !SetRtcpParams(selected_crypto_suite, &rtcp_send_key[0], + static_cast<int>(rtcp_send_key.size()), send_extension_ids, + selected_crypto_suite, &rtcp_recv_key[0], + static_cast<int>(rtcp_recv_key.size()), + recv_extension_ids)) { + RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTCP failed"; + } +} + +bool DtlsSrtpTransport::ExtractParams( + cricket::DtlsTransportInternal* dtls_transport, + int* selected_crypto_suite, + rtc::ZeroOnFreeBuffer<unsigned char>* send_key, + rtc::ZeroOnFreeBuffer<unsigned char>* 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 (!rtc::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) + rtc::ZeroOnFreeBuffer<unsigned char> dtls_buffer(key_len * 2 + salt_len * 2); + + // RFC 5705 exporter using the RFC 5764 parameters + if (!dtls_transport->ExportKeyingMaterial(kDtlsSrtpExporterLabel, NULL, 0, + false, &dtls_buffer[0], + dtls_buffer.size())) { + RTC_LOG(LS_WARNING) << "DTLS-SRTP key export failed"; + RTC_DCHECK_NOTREACHED(); // This should never happen + return false; + } + + // Sync up the keys with the DTLS-SRTP interface + rtc::ZeroOnFreeBuffer<unsigned char> client_write_key(key_len + salt_len); + rtc::ZeroOnFreeBuffer<unsigned char> server_write_key(key_len + salt_len); + size_t offset = 0; + memcpy(&client_write_key[0], &dtls_buffer[offset], key_len); + offset += key_len; + memcpy(&server_write_key[0], &dtls_buffer[offset], key_len); + offset += key_len; + memcpy(&client_write_key[key_len], &dtls_buffer[offset], salt_len); + offset += salt_len; + memcpy(&server_write_key[key_len], &dtls_buffer[offset], salt_len); + + rtc::SSLRole role; + if (!dtls_transport->GetDtlsRole(&role)) { + RTC_LOG(LS_WARNING) << "Failed to get the DTLS role."; + return false; + } + + if (role == rtc::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::SetDtlsTransport( + cricket::DtlsTransportInternal* new_dtls_transport, + cricket::DtlsTransportInternal** old_dtls_transport) { + if (*old_dtls_transport == new_dtls_transport) { + return; + } + + if (*old_dtls_transport) { + (*old_dtls_transport)->UnsubscribeDtlsTransportState(this); + } + + *old_dtls_transport = new_dtls_transport; + + if (new_dtls_transport) { + new_dtls_transport->SubscribeDtlsTransportState( + this, + [this](cricket::DtlsTransportInternal* transport, + DtlsTransportState state) { OnDtlsState(transport, state); }); + } +} + +void DtlsSrtpTransport::SetRtpDtlsTransport( + cricket::DtlsTransportInternal* rtp_dtls_transport) { + SetDtlsTransport(rtp_dtls_transport, &rtp_dtls_transport_); +} + +void DtlsSrtpTransport::SetRtcpDtlsTransport( + cricket::DtlsTransportInternal* rtcp_dtls_transport) { + SetDtlsTransport(rtcp_dtls_transport, &rtcp_dtls_transport_); +} + +void DtlsSrtpTransport::OnDtlsState(cricket::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(); + return; + } + + MaybeSetupDtlsSrtp(); +} + +void DtlsSrtpTransport::OnWritableState( + rtc::PacketTransportInternal* packet_transport) { + MaybeSetupDtlsSrtp(); +} + +void DtlsSrtpTransport::SetOnDtlsStateChange( + std::function<void(void)> callback) { + on_dtls_state_change_ = std::move(callback); +} +} // namespace webrtc |