diff --git a/api/BUILD.gn b/api/BUILD.gn index 9a9b2c3ff6..711b174fc4 100644 --- a/api/BUILD.gn +++ b/api/BUILD.gn @@ -274,6 +274,8 @@ rtc_library("libjingle_peerconnection_api") { visibility = [ "*" ] cflags = [] sources = [ + # RingRTC change: Propagate externally-negotiated keys. + "crypto_params.h", "data_channel_interface.cc", "data_channel_interface.h", # RingRTC change to add ICE forking diff --git a/api/crypto_params.h b/api/crypto_params.h new file mode 100644 index 0000000000..615ea3e25d --- /dev/null +++ b/api/crypto_params.h @@ -0,0 +1,44 @@ +/* + * Copyright 2019-2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +// RingRTC change: Struct to carry SRTP crypto parameters to RTP transport. + +#ifndef API_CRYPTO_PARAMS_H_ +#define API_CRYPTO_PARAMS_H_ + +#include + +#include "rtc_base/buffer.h" +#include "rtc_base/ssl_stream_adapter.h" // kSrtpInvalidCryptoSuite + +namespace cricket { + +// Parameters for propagating SRTP params to RTP transport. +struct CryptoParams { + CryptoParams() = default; + + // Manually define a copy constructor because ZeroOnFreeBuffer assumes its + // contents might be quite large, and wants us to be explicit. However, keys + // won't be extremely large, so allow copies. + CryptoParams(const CryptoParams& other) + : crypto_suite(other.crypto_suite), + key_params(other.key_params.data(), other.key_params.size()) {} + + // Similarly define an assignment constructor. + CryptoParams& operator=(CryptoParams other) { + std::swap(crypto_suite, other.crypto_suite); + // ZeroOnFreeBuffer defines a swap() + std::swap(key_params, other.key_params); + return *this; + } + + int crypto_suite = rtc::kSrtpInvalidCryptoSuite; + // Key and salt. + rtc::ZeroOnFreeBuffer key_params; +}; + +} // namespace cricket + +#endif // API_CRYPTO_PARAMS_H_ diff --git a/p2p/base/transport_description_factory.cc b/p2p/base/transport_description_factory.cc index 6e3af94384..07b6f63a64 100644 --- a/p2p/base/transport_description_factory.cc +++ b/p2p/base/transport_description_factory.cc @@ -23,7 +23,8 @@ namespace cricket { TransportDescriptionFactory::TransportDescriptionFactory( const webrtc::FieldTrialsView& field_trials) - : field_trials_(field_trials) {} + // RingRTC: Allow out-of-band / "manual" key negotiation. + : manually_specify_keys_(false), field_trials_(field_trials) {} TransportDescriptionFactory::~TransportDescriptionFactory() = default; @@ -52,6 +53,12 @@ std::unique_ptr TransportDescriptionFactory::CreateOffer( if (insecure_ && !certificate_) { return desc; } + + // RingRTC: Allow out-of-band / "manual" key negotiation. + if (manually_specify_keys_) { + RTC_LOG(LS_INFO) << "Skipping SetSecurityInfo because expect manual keys"; + return desc; + } // Fail if we can't create the fingerprint. // If we are the initiator set role to "actpass". if (!SetSecurityInfo(desc.get(), CONNECTIONROLE_ACTPASS)) { @@ -95,6 +102,12 @@ std::unique_ptr TransportDescriptionFactory::CreateAnswer( if ((!certificate_ || !offer->identity_fingerprint.get()) && insecure()) { return desc; } + // RingRTC: Allow out-of-band / "manual" key negotiation. + if (manually_specify_keys_) { + RTC_LOG(LS_INFO) << "Skipping SetSecurityInfo because expect manual keys"; + return desc; + } + if (!offer->identity_fingerprint.get()) { if (require_transport_attributes) { // We require DTLS, but the other side didn't offer it. Fail. diff --git a/p2p/base/transport_description_factory.h b/p2p/base/transport_description_factory.h index bd0cb9078e..e08781b703 100644 --- a/p2p/base/transport_description_factory.h +++ b/p2p/base/transport_description_factory.h @@ -43,12 +43,17 @@ class TransportDescriptionFactory { const webrtc::FieldTrialsView& field_trials); ~TransportDescriptionFactory(); + // RingRTC: Allow out-of-band / "manual" key negotiation. + bool manually_specify_keys() const { return manually_specify_keys_; } // The certificate to use when setting up DTLS. const rtc::scoped_refptr& certificate() const { return certificate_; } - // Specifies the certificate to use + // RingRTC: Allow out-of-band / "manual" key negotiation. + // Specifies that keys should be manually specified. + void set_manually_specify_keys(bool b) { manually_specify_keys_ = b; } + // Specifies the certificate to use (only used when !manually_specify_keys). void set_certificate(rtc::scoped_refptr certificate) { certificate_ = std::move(certificate); } @@ -85,6 +90,11 @@ class TransportDescriptionFactory { bool SetSecurityInfo(TransportDescription* description, ConnectionRole role) const; bool insecure_ = false; + + // RingRTC: Allow out-of-band / "manual" key negotiation. + // True iff keys should be manually specified (e.g. negotiated out of band, + // and not via DTLS). + bool manually_specify_keys_ = false; rtc::scoped_refptr certificate_; const webrtc::FieldTrialsView& field_trials_; }; diff --git a/pc/BUILD.gn b/pc/BUILD.gn index d119de6ace..f49b0bfc5b 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -213,6 +213,8 @@ rtc_source_set("jsep_transport") { ":rtp_transport_internal", ":sctp_transport", ":session_description", + # RingRTC: Allow out-of-band / "manual" key negotiation. + ":srtp_key_carrier", ":srtp_transport", ":transport_stats", "../api:array_view", @@ -538,6 +540,21 @@ rtc_source_set("sctp_utils") { ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } +# RingRTC: Allow out-of-band / "manual" key negotiation. +rtc_source_set("srtp_key_carrier") { + visibility = [ ":*" ] + sources = [ + "srtp_key_carrier.cc", + "srtp_key_carrier.h", + ] + deps = [ + ":session_description", + "../api:libjingle_peerconnection_api", + "../rtc_base:logging", + "../rtc_base:ssl", + ] +} + rtc_source_set("srtp_session") { visibility = [ ":*" ] sources = [ diff --git a/pc/jsep_transport.cc b/pc/jsep_transport.cc index faff4e8cf4..a8f79ce964 100644 --- a/pc/jsep_transport.cc +++ b/pc/jsep_transport.cc @@ -36,10 +36,14 @@ JsepTransportDescription::JsepTransportDescription() {} JsepTransportDescription::JsepTransportDescription( bool rtcp_mux_enabled, + // RingRTC: Allow out-of-band / "manual" key negotiation. + const std::optional& crypto, const std::vector& encrypted_header_extension_ids, int rtp_abs_sendtime_extn_id, const TransportDescription& transport_desc) : rtcp_mux_enabled(rtcp_mux_enabled), + // RingRTC: Allow out-of-band / "manual" key negotiation. + crypto(crypto), encrypted_header_extension_ids(encrypted_header_extension_ids), rtp_abs_sendtime_extn_id(rtp_abs_sendtime_extn_id), transport_desc(transport_desc) {} @@ -47,6 +51,8 @@ JsepTransportDescription::JsepTransportDescription( JsepTransportDescription::JsepTransportDescription( const JsepTransportDescription& from) : rtcp_mux_enabled(from.rtcp_mux_enabled), + // RingRTC: Allow out-of-band / "manual" key negotiation. + crypto(from.crypto), encrypted_header_extension_ids(from.encrypted_header_extension_ids), rtp_abs_sendtime_extn_id(from.rtp_abs_sendtime_extn_id), transport_desc(from.transport_desc) {} @@ -59,6 +65,8 @@ JsepTransportDescription& JsepTransportDescription::operator=( return *this; } rtcp_mux_enabled = from.rtcp_mux_enabled; + // RingRTC: Allow out-of-band / "manual" key negotiation. + crypto = from.crypto; encrypted_header_extension_ids = from.encrypted_header_extension_ids; rtp_abs_sendtime_extn_id = from.rtp_abs_sendtime_extn_id; transport_desc = from.transport_desc; @@ -72,7 +80,8 @@ JsepTransport::JsepTransport( rtc::scoped_refptr ice_transport, rtc::scoped_refptr rtcp_ice_transport, std::unique_ptr unencrypted_rtp_transport, - std::unique_ptr sdes_transport, + // RingRTC: Allow out-of-band / "manual" key negotiation. + std::unique_ptr srtp_transport, std::unique_ptr dtls_srtp_transport, std::unique_ptr rtp_dtls_transport, std::unique_ptr rtcp_dtls_transport, @@ -84,7 +93,8 @@ JsepTransport::JsepTransport( ice_transport_(std::move(ice_transport)), rtcp_ice_transport_(std::move(rtcp_ice_transport)), unencrypted_rtp_transport_(std::move(unencrypted_rtp_transport)), - sdes_transport_(std::move(sdes_transport)), + // RingRTC: Allow out-of-band / "manual" key negotiation. + srtp_transport_(std::move(srtp_transport)), dtls_srtp_transport_(std::move(dtls_srtp_transport)), rtp_dtls_transport_(rtp_dtls_transport ? rtc::make_ref_counted( @@ -108,15 +118,18 @@ JsepTransport::JsepTransport( (rtcp_dtls_transport_ != nullptr)); // Verify the "only one out of these three can be set" invariant. if (unencrypted_rtp_transport_) { - RTC_DCHECK(!sdes_transport); + // RingRTC: Allow out-of-band / "manual" key negotiation. + RTC_DCHECK(!srtp_transport); RTC_DCHECK(!dtls_srtp_transport); - } else if (sdes_transport_) { + } else if (srtp_transport_) { + // RingRTC: Allow out-of-band / "manual" key negotiation. RTC_DCHECK(!unencrypted_rtp_transport); RTC_DCHECK(!dtls_srtp_transport); } else { RTC_DCHECK(dtls_srtp_transport_); RTC_DCHECK(!unencrypted_rtp_transport); - RTC_DCHECK(!sdes_transport); + // RingRTC: Allow out-of-band / "manual" key negotiation. + RTC_DCHECK(!srtp_transport); } if (sctp_transport_) { @@ -163,9 +176,21 @@ webrtc::RTCError JsepTransport::SetLocalJsepTransportDescription( "Failed to setup RTCP mux."); } - if (dtls_srtp_transport_) { + // RingRTC: Allow out-of-band / "manual" key negotiation. + // If doing SRTP with manual keys, setup the crypto parameters. + if (srtp_transport_) { RTC_DCHECK(!unencrypted_rtp_transport_); - RTC_DCHECK(!sdes_transport_); + RTC_DCHECK(!dtls_srtp_transport_); + if (!SetSrtpCrypto(jsep_description.crypto, + jsep_description.encrypted_header_extension_ids, type, + ContentSource::CS_LOCAL)) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Failed to setup SRTP crypto parameters."); + } + } else if (dtls_srtp_transport_) { + RTC_DCHECK(!unencrypted_rtp_transport_); + // RingRTC: Allow out-of-band / "manual" key negotiation. + RTC_DCHECK(!srtp_transport_); dtls_srtp_transport_->UpdateRecvEncryptedHeaderExtensionIds( jsep_description.encrypted_header_extension_ids); } @@ -240,9 +265,22 @@ webrtc::RTCError JsepTransport::SetRemoteJsepTransportDescription( "Failed to setup RTCP mux."); } - if (dtls_srtp_transport_) { + // RingRTC: Allow out-of-band / "manual" key negotiation. + // If doing SRTP, setup the SRTP crypto parameters. + if (srtp_transport_) { RTC_DCHECK(!unencrypted_rtp_transport_); - RTC_DCHECK(!sdes_transport_); + RTC_DCHECK(!dtls_srtp_transport_); + if (!SetSrtpCrypto(jsep_description.crypto, + jsep_description.encrypted_header_extension_ids, type, + ContentSource::CS_REMOTE)) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Failed to setup SRTP crypto parameters."); + } + srtp_transport_->CacheRtpAbsSendTimeHeaderExtension( + jsep_description.rtp_abs_sendtime_extn_id); + } else if (dtls_srtp_transport_) { + RTC_DCHECK(!unencrypted_rtp_transport_); + RTC_DCHECK(!srtp_transport_); dtls_srtp_transport_->UpdateSendEncryptedHeaderExtensionIds( jsep_description.encrypted_header_extension_ids); dtls_srtp_transport_->CacheRtpAbsSendTimeHeaderExtension( @@ -429,17 +467,20 @@ bool JsepTransport::SetRtcpMux(bool enable, void JsepTransport::ActivateRtcpMux() { if (unencrypted_rtp_transport_) { - RTC_DCHECK(!sdes_transport_); + // RingRTC: Allow out-of-band / "manual" key negotiation. + RTC_DCHECK(!srtp_transport_); RTC_DCHECK(!dtls_srtp_transport_); unencrypted_rtp_transport_->SetRtcpPacketTransport(nullptr); - } else if (sdes_transport_) { + } else if (srtp_transport_) { + // RingRTC: Allow out-of-band / "manual" key negotiation. RTC_DCHECK(!unencrypted_rtp_transport_); RTC_DCHECK(!dtls_srtp_transport_); - sdes_transport_->SetRtcpPacketTransport(nullptr); + srtp_transport_->SetRtcpPacketTransport(nullptr); } else if (dtls_srtp_transport_) { RTC_DCHECK(dtls_srtp_transport_); RTC_DCHECK(!unencrypted_rtp_transport_); - RTC_DCHECK(!sdes_transport_); + // RingRTC: Allow out-of-band / "manual" key negotiation. + RTC_DCHECK(!srtp_transport_); dtls_srtp_transport_->SetDtlsTransports(rtp_dtls_transport(), /*rtcp_dtls_transport=*/nullptr); } @@ -448,6 +489,46 @@ void JsepTransport::ActivateRtcpMux() { rtcp_mux_active_callback_(); } +// RingRTC: Allow out-of-band / "manual" key negotiation. +bool JsepTransport::SetSrtpCrypto( + const std::optional& crypto, + const std::vector& encrypted_extension_ids, + webrtc::SdpType type, + ContentSource source) { + RTC_DCHECK_RUN_ON(network_thread_); + if (!crypto.has_value()) { + RTC_LOG(LS_ERROR) << "Setting manually-specified SRTP without any keys"; + return false; + } + if (!srtp_key_carrier_.ApplyParams(crypto.value(), type, source)) { + return false; + } + + if (source == ContentSource::CS_LOCAL) { + recv_extension_ids_ = encrypted_extension_ids; + } else { + send_extension_ids_ = encrypted_extension_ids; + } + + // If appropriate, apply the negotiated parameters + // to the SRTP transport. + if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { + const CryptoParams& send = srtp_key_carrier_.send_params(); + const CryptoParams& recv = srtp_key_carrier_.recv_params(); + RTC_DCHECK(send_extension_ids_); + RTC_DCHECK(recv_extension_ids_); + return srtp_transport_->SetRtpParams( + send.crypto_suite, + send.key_params.data(), + static_cast(send.key_params.size()), + *(send_extension_ids_), recv.crypto_suite, + recv.key_params.data(), + static_cast(recv.key_params.size()), + *(recv_extension_ids_)); + } + return true; +} + webrtc::RTCError JsepTransport::NegotiateAndSetDtlsParameters( SdpType local_description_type) { RTC_DCHECK_RUN_ON(network_thread_); diff --git a/pc/jsep_transport.h b/pc/jsep_transport.h index af0c797fc8..643493287f 100644 --- a/pc/jsep_transport.h +++ b/pc/jsep_transport.h @@ -17,6 +17,11 @@ #include #include +// RingRTC: Allow out-of-band / "manual" key negotiation. +#include +#include "api/crypto_params.h" +#include "pc/srtp_key_carrier.h" + #include "absl/types/optional.h" #include "api/candidate.h" #include "api/ice_transport_interface.h" @@ -57,6 +62,8 @@ struct JsepTransportDescription { JsepTransportDescription(); JsepTransportDescription( bool rtcp_mux_enabled, + // RingRTC: Allow out-of-band / "manual" key negotiation. + const std::optional& crypto, const std::vector& encrypted_header_extension_ids, int rtp_abs_sendtime_extn_id, const TransportDescription& transport_description); @@ -66,6 +73,8 @@ struct JsepTransportDescription { JsepTransportDescription& operator=(const JsepTransportDescription& from); bool rtcp_mux_enabled = true; + // RingRTC: Allow out-of-band / "manual" key negotiation. + std::optional crypto; std::vector encrypted_header_extension_ids; int rtp_abs_sendtime_extn_id = -1; // TODO(zhihuang): Add the ICE and DTLS related variables and methods from @@ -92,7 +101,8 @@ class JsepTransport { rtc::scoped_refptr ice_transport, rtc::scoped_refptr rtcp_ice_transport, std::unique_ptr unencrypted_rtp_transport, - std::unique_ptr sdes_transport, + // RingRTC: Allow out-of-band / "manual" key negotiation. + std::unique_ptr srtp_transport, std::unique_ptr dtls_srtp_transport, std::unique_ptr rtp_dtls_transport, std::unique_ptr rtcp_dtls_transport, @@ -169,8 +179,9 @@ class JsepTransport { if (dtls_srtp_transport_) { return dtls_srtp_transport_.get(); } - if (sdes_transport_) { - return sdes_transport_.get(); + // RingRTC: Allow out-of-band / "manual" key negotiation. + if (srtp_transport_) { + return srtp_transport_.get(); } if (unencrypted_rtp_transport_) { return unencrypted_rtp_transport_.get(); @@ -239,6 +250,12 @@ class JsepTransport { void ActivateRtcpMux() RTC_RUN_ON(network_thread_); + // RingRTC: Allow out-of-band / "manual" key negotiation. + bool SetSrtpCrypto(const std::optional& crypto, + const std::vector& encrypted_extension_ids, + webrtc::SdpType type, + ContentSource source); + // Negotiates and sets the DTLS parameters based on the current local and // remote transport description, such as the DTLS role to use, and whether // DTLS should be activated. @@ -290,7 +307,8 @@ class JsepTransport { // To avoid downcasting and make it type safe, keep three unique pointers for // different SRTP mode and only one of these is non-nullptr. const std::unique_ptr unencrypted_rtp_transport_; - const std::unique_ptr sdes_transport_; + // RingRTC: Allow out-of-band / "manual" key negotiation. + const std::unique_ptr srtp_transport_; const std::unique_ptr dtls_srtp_transport_; const rtc::scoped_refptr rtp_dtls_transport_; @@ -301,6 +319,8 @@ class JsepTransport { const rtc::scoped_refptr sctp_transport_; + // RingRTC: Allow out-of-band / "manual" key negotiation. + SrtpKeyCarrier srtp_key_carrier_ RTC_GUARDED_BY(network_thread_); RtcpMuxFilter rtcp_mux_negotiator_ RTC_GUARDED_BY(network_thread_); // Cache the encrypted header extension IDs for SDES negoitation. diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc index 1b2f73cad1..de60ca248a 100644 --- a/pc/jsep_transport_controller.cc +++ b/pc/jsep_transport_controller.cc @@ -527,7 +527,8 @@ JsepTransportController::CreateUnencryptedRtpTransport( return unencrypted_rtp_transport; } -std::unique_ptr JsepTransportController::CreateSdesTransport( +// RingRTC: Allow out-of-band / "manual" key negotiation. +std::unique_ptr JsepTransportController::CreateSrtpTransport( const std::string& transport_name, cricket::DtlsTransportInternal* rtp_dtls_transport, cricket::DtlsTransportInternal* rtcp_dtls_transport) { @@ -987,9 +988,10 @@ JsepTransportController::CreateJsepTransportDescription( ? true : content_desc->rtcp_mux(); + // RingRTC: Allow out-of-band / "manual" key negotiation. return cricket::JsepTransportDescription( - rtcp_mux_enabled, encrypted_extension_ids, rtp_abs_sendtime_extn_id, - transport_info.description); + rtcp_mux_enabled, content_desc->crypto(), encrypted_extension_ids, + rtp_abs_sendtime_extn_id, transport_info.description); } std::vector JsepTransportController::GetEncryptedHeaderExtensionIds( @@ -1095,6 +1097,13 @@ RTCError JsepTransportController::MaybeCreateJsepTransport( if (transport) { return RTCError::OK(); } + // RingRTC: Allow out-of-band / "manual" key negotiation. + const cricket::MediaContentDescription* content_desc = + content_info.media_description(); + if (certificate_ && content_desc->crypto().has_value()) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "Manual keys and DTLS-SRTP cannot be enabled at the same time."); + } rtc::scoped_refptr ice = CreateIceTransport(content_info.name, /*rtcp=*/false); @@ -1104,7 +1113,8 @@ RTCError JsepTransportController::MaybeCreateJsepTransport( std::unique_ptr rtcp_dtls_transport; std::unique_ptr unencrypted_rtp_transport; - std::unique_ptr sdes_transport; + // RingRTC: Allow out-of-band / "manual" key negotiation. + std::unique_ptr srtp_transport; std::unique_ptr dtls_srtp_transport; rtc::scoped_refptr rtcp_ice; @@ -1121,6 +1131,11 @@ RTCError JsepTransportController::MaybeCreateJsepTransport( << "Creating UnencryptedRtpTransport, becayse encryption is disabled."; unencrypted_rtp_transport = CreateUnencryptedRtpTransport( content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); + } else if (content_desc->crypto().has_value()) { + // RingRTC: Allow out-of-band / "manual" key negotiation. + srtp_transport = CreateSrtpTransport( + content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); + RTC_LOG(LS_INFO) << "Creating SrtpTransport."; } else { RTC_LOG(LS_INFO) << "Creating DtlsSrtpTransport."; dtls_srtp_transport = CreateDtlsSrtpTransport( @@ -1136,7 +1151,8 @@ RTCError JsepTransportController::MaybeCreateJsepTransport( std::unique_ptr jsep_transport = std::make_unique( content_info.name, certificate_, std::move(ice), std::move(rtcp_ice), - std::move(unencrypted_rtp_transport), std::move(sdes_transport), + // RingRTC: Allow out-of-band / "manual" key negotiation. + std::move(unencrypted_rtp_transport), std::move(srtp_transport), std::move(dtls_srtp_transport), std::move(rtp_dtls_transport), std::move(rtcp_dtls_transport), std::move(sctp_transport), [&]() { RTC_DCHECK_RUN_ON(network_thread_); diff --git a/pc/jsep_transport_controller.h b/pc/jsep_transport_controller.h index 6646936c89..3f9f0cbf83 100644 --- a/pc/jsep_transport_controller.h +++ b/pc/jsep_transport_controller.h @@ -450,7 +450,8 @@ class JsepTransportController : public sigslot::has_slots<> { const std::string& transport_name, rtc::PacketTransportInternal* rtp_packet_transport, rtc::PacketTransportInternal* rtcp_packet_transport); - std::unique_ptr CreateSdesTransport( + // RingRTC: Allow out-of-band / "manual" key negotiation. + std::unique_ptr CreateSrtpTransport( const std::string& transport_name, cricket::DtlsTransportInternal* rtp_dtls_transport, cricket::DtlsTransportInternal* rtcp_dtls_transport); diff --git a/pc/media_session.cc b/pc/media_session.cc index 9c5463935b..9392975eea 100644 --- a/pc/media_session.cc +++ b/pc/media_session.cc @@ -1161,8 +1161,12 @@ bool IsMediaProtocolSupported(MediaType type, } } -void SetMediaProtocol(bool secure_transport, MediaContentDescription* desc) { - if (secure_transport) +// RingRTC: Allow out-of-band / "manual" key negotiation. +void SetMediaProtocol(bool secure_transport, bool manually_specify_keys, + MediaContentDescription* desc) { + if (desc->crypto().has_value() || manually_specify_keys) + desc->set_protocol(kMediaProtocolSavpf); + else if (secure_transport) desc->set_protocol(kMediaProtocolDtlsSavpf); else desc->set_protocol(kMediaProtocolAvpf); @@ -2090,7 +2094,9 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForOffer( // Insecure transport should only occur in testing. bool secure_transport = !(transport_desc_factory_->insecure()); - SetMediaProtocol(secure_transport, content_description.get()); + // RingRTC: Allow out-of-band / "manual" key negotiation. + SetMediaProtocol(secure_transport, manually_specify_keys(), + content_description.get()); content_description->set_direction(media_description_options.direction); diff --git a/pc/media_session.h b/pc/media_session.h index 1dd5899c72..87ad1022dc 100644 --- a/pc/media_session.h +++ b/pc/media_session.h @@ -161,6 +161,9 @@ class MediaSessionDescriptionFactory { const VideoCodecs& recv_codecs); RtpHeaderExtensions filtered_rtp_header_extensions( RtpHeaderExtensions extensions) const; + // RingRTC: Allow out-of-band / "manual" key negotiation. + bool manually_specify_keys() const { return manually_specify_keys_; } + void set_manually_specify_keys(bool b) { manually_specify_keys_ = b; } void set_enable_encrypted_rtp_header_extensions(bool enable) { enable_encrypted_rtp_header_extensions_ = enable; @@ -319,6 +322,8 @@ class MediaSessionDescriptionFactory { webrtc::AlwaysValidPointer const ssrc_generator_; bool enable_encrypted_rtp_header_extensions_ = false; + // RingRTC: Allow out-of-band / "manual" key negotiation. + bool manually_specify_keys_ = false; const TransportDescriptionFactory* transport_desc_factory_; }; diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index 3f5811054e..2c756016f3 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -2734,9 +2734,11 @@ void PeerConnection::ReportRemoteIceCandidateAdded( } } +// RingRTC: Allow out-of-band / "manual" key negotiation. bool PeerConnection::SrtpRequired() const { RTC_DCHECK_RUN_ON(signaling_thread()); - return dtls_enabled_; + return (dtls_enabled_ || + sdp_handler_->webrtc_session_desc_factory()->ManuallySpecifyKeys()); } void PeerConnection::OnTransportControllerGatheringState( diff --git a/pc/sdp_offer_answer.cc b/pc/sdp_offer_answer.cc index 1eb220729c..6cd25a9711 100644 --- a/pc/sdp_offer_answer.cc +++ b/pc/sdp_offer_answer.cc @@ -286,8 +286,7 @@ RTCError VerifyCrypto(const SessionDescription* desc, continue; } #if !defined(WEBRTC_FUCHSIA) - // RingRTC change to always disable DTLS. - // RTC_CHECK(dtls_enabled) << "SDES protocol is only allowed in Fuchsia"; + RTC_CHECK(dtls_enabled) << "SDES protocol is only allowed in Fuchsia"; #endif const std::string& mid = content_info.name; auto it = bundle_groups_by_mid.find(mid); @@ -1397,6 +1396,8 @@ void SdpOfferAnswerHandler::Initialize( RTC_LOG(LS_INFO) << "Disabling encryption. This should only be done in tests."; webrtc_session_desc_factory_->SetInsecureForTesting(); + // RingRTC: Allow out-of-band / "manual" key negotiation. + webrtc_session_desc_factory_->SetManuallySpecifyKeys(false); } webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions( @@ -3559,7 +3560,11 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription( // Verify crypto settings. std::string crypto_error; - if (pc_->dtls_enabled()) { + // RingRTC: Allow out-of-band / "manual" key negotiation. + // Do not verify if "ManuallySpecifyKeys" is set; `VerifyCrypto` only makes + // sense for DTLS. + if (!webrtc_session_desc_factory_->ManuallySpecifyKeys() && + pc_->dtls_enabled()) { RTCError crypto_error = VerifyCrypto( sdesc->description(), pc_->dtls_enabled(), bundle_groups_by_mid); if (!crypto_error.ok()) { diff --git a/pc/session_description.h b/pc/session_description.h index fe037a5786..40c6ab14b5 100644 --- a/pc/session_description.h +++ b/pc/session_description.h @@ -23,6 +23,8 @@ #include "absl/memory/memory.h" #include "absl/strings/string_view.h" +// RingRTC: Allow out-of-band / "manual" key negotiation. +#include "api/crypto_params.h" #include "api/media_types.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" @@ -121,6 +123,12 @@ class MediaContentDescription { bandwidth_type_ = bandwidth_type; } + // RingRTC: Allow out-of-band / "manual" key negotiation. + const absl::optional& crypto() const { return crypto_; } + void set_crypto(const absl::optional& crypto) { + crypto_ = crypto; + } + // List of RTP header extensions. URIs are **NOT** guaranteed to be unique // as they can appear twice when both encrypted and non-encrypted extensions // are present. @@ -260,6 +268,8 @@ class MediaContentDescription { int bandwidth_ = kAutoBandwidth; std::string bandwidth_type_ = kApplicationSpecificBandwidth; + // RingRTC: Allow out-of-band / "manual" key negotiation. + absl::optional crypto_; std::vector rtp_header_extensions_; bool rtp_header_extensions_set_ = false; StreamParamsVec send_streams_; diff --git a/pc/srtp_key_carrier.cc b/pc/srtp_key_carrier.cc new file mode 100644 index 0000000000..b278b3868c --- /dev/null +++ b/pc/srtp_key_carrier.cc @@ -0,0 +1,67 @@ +/* + * Copyright 2009 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. + */ + +// RingRTC: Allow out-of-band / "manual" key negotiation. + +#include "pc/srtp_key_carrier.h" + +#include "rtc_base/logging.h" +#include "rtc_base/ssl_stream_adapter.h" + +namespace cricket { + +SrtpKeyCarrier::SrtpKeyCarrier() = default; + +SrtpKeyCarrier::~SrtpKeyCarrier() = default; + +bool SrtpKeyCarrier::ApplyParams(const CryptoParams& crypto, + webrtc::SdpType type, + ContentSource source) { + switch (type) { + case webrtc::SdpType::kOffer: + offer_params_ = crypto; + return true; + case webrtc::SdpType::kPrAnswer: + case webrtc::SdpType::kAnswer: + return SetAnswer(crypto, source); + default: + return false; + } +} + +bool SrtpKeyCarrier::SetAnswer(const CryptoParams& answer_params, + ContentSource source) { + if (!offer_params_.has_value()) { + RTC_LOG(LS_WARNING) << "Missing offer parameters when handling SRTP answer"; + return false; + } + + const CryptoParams& new_send_params = + (source == CS_REMOTE) ? offer_params_.value() : answer_params; + const CryptoParams& new_recv_params = + (source == CS_REMOTE) ? answer_params : offer_params_.value(); + + if (new_send_params.crypto_suite == rtc::kSrtpInvalidCryptoSuite) { + RTC_LOG(LS_WARNING) << "Invalid crypto suite(s) received for send"; + return false; + } + if (new_recv_params.crypto_suite == rtc::kSrtpInvalidCryptoSuite) { + RTC_LOG(LS_WARNING) << "Invalid crypto suite(s) received for recv"; + return false; + } + + applied_send_params_ = new_send_params; + applied_recv_params_ = new_recv_params; + + offer_params_ = std::nullopt; + return true; +} + +} // namespace cricket diff --git a/pc/srtp_key_carrier.h b/pc/srtp_key_carrier.h new file mode 100644 index 0000000000..924bdda04a --- /dev/null +++ b/pc/srtp_key_carrier.h @@ -0,0 +1,51 @@ +/* + * Copyright 2009 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. + */ + +// RingRTC: Allow out-of-band / "manual" key negotiation. + +#ifndef PC_SRTP_KEY_CARRIER_H_ +#define PC_SRTP_KEY_CARRIER_H_ + +#include + +#include "api/crypto_params.h" +#include "api/jsep.h" +#include "pc/session_description.h" + +namespace cricket { + +// A helper class used to propagate crypto params. +class SrtpKeyCarrier { + public: + SrtpKeyCarrier(); + ~SrtpKeyCarrier(); + + // Handle the offer/answer propagation of the crypto parameters. + // If type is kPrAnswer or kAnswer, returns true iff `send_params` and + // `recv_params` are usable. + bool ApplyParams(const CryptoParams& crypto, + webrtc::SdpType type, + ContentSource source); + + const CryptoParams& send_params() { return applied_send_params_; } + const CryptoParams& recv_params() { return applied_recv_params_; } + + private: + // Applies params to be visible from `send_params` and `recv_params`. + bool SetAnswer(const CryptoParams& answer_params, ContentSource source); + + std::optional offer_params_; + CryptoParams applied_send_params_; + CryptoParams applied_recv_params_; +}; + +} // namespace cricket + +#endif // PC_SRTP_KEY_CARRIER_H_ diff --git a/pc/webrtc_session_description_factory.cc b/pc/webrtc_session_description_factory.cc index 9919260aa3..f454a09379 100644 --- a/pc/webrtc_session_description_factory.cc +++ b/pc/webrtc_session_description_factory.cc @@ -129,9 +129,14 @@ WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory( if (!dtls_enabled) { RTC_LOG(LS_INFO) << "DTLS-SRTP disabled"; - transport_desc_factory_.SetInsecureForTesting(); + // RingRTC: Allow out-of-band / "manual" key negotiation. + SetManuallySpecifyKeys(true); return; } + + // RingRTC: Allow out-of-band / "manual" key negotiation. + // Manual keys are disabled if DTLS is on. + SetManuallySpecifyKeys(false); if (certificate) { // Use `certificate`. certificate_request_state_ = CERTIFICATE_WAITING; @@ -256,6 +261,16 @@ void WebRtcSessionDescriptionFactory::CreateAnswer( } } +// RingRTC: Allow out-of-band / "manual" key negotiation. +void WebRtcSessionDescriptionFactory::SetManuallySpecifyKeys(bool b) { + transport_desc_factory_.set_manually_specify_keys(b); + session_desc_factory_.set_manually_specify_keys(b); +} + +bool WebRtcSessionDescriptionFactory::ManuallySpecifyKeys() const { + return session_desc_factory_.manually_specify_keys(); +} + void WebRtcSessionDescriptionFactory::InternalCreateOffer( CreateSessionDescriptionRequest request) { if (sdp_info_->local_description()) { diff --git a/pc/webrtc_session_description_factory.h b/pc/webrtc_session_description_factory.h index eed922eda7..a39ca05ea8 100644 --- a/pc/webrtc_session_description_factory.h +++ b/pc/webrtc_session_description_factory.h @@ -73,6 +73,10 @@ class WebRtcSessionDescriptionFactory { void CreateAnswer(CreateSessionDescriptionObserver* observer, const cricket::MediaSessionOptions& session_options); + // RingRTC: Allow out-of-band / "manual" key negotiation. + void SetManuallySpecifyKeys(bool b); + bool ManuallySpecifyKeys() const; + void set_enable_encrypted_rtp_header_extensions(bool enable) { session_desc_factory_.set_enable_encrypted_rtp_header_extensions(enable); } diff --git a/ringrtc/rffi/src/peer_connection.cc b/ringrtc/rffi/src/peer_connection.cc index c1dd45a220..6a2ea8f65d 100644 --- a/ringrtc/rffi/src/peer_connection.cc +++ b/ringrtc/rffi/src/peer_connection.cc @@ -21,7 +21,6 @@ #include "rffi/src/stats_observer.h" #include "rtc_base/message_digest.h" #include "rtc_base/string_encode.h" -#include "rtc_base/third_party/base64/base64.h" #include "system_wrappers/include/field_trial.h" #include @@ -201,11 +200,10 @@ Rust_disableDtlsAndSetSrtpKey(webrtc::SessionDescriptionInterface* session_descr } cricket::CryptoParams crypto_params; - crypto_params.crypto_suite = rtc::SrtpCryptoSuiteToName(crypto_suite); + crypto_params.crypto_suite = crypto_suite; - std::string key(key_borrowed, key_len); - std::string salt(salt_borrowed, salt_len); - crypto_params.key_params = "inline:" + rtc::Base64::Encode(key + salt); + crypto_params.key_params.SetData(key_borrowed, key_len); + crypto_params.key_params.AppendData(salt_borrowed, salt_len); // Disable DTLS for (cricket::TransportInfo& transport : session->transport_infos()) { @@ -218,9 +216,7 @@ Rust_disableDtlsAndSetSrtpKey(webrtc::SessionDescriptionInterface* session_descr cricket::MediaContentDescription* media = content.media_description(); if (media) { media->set_protocol(cricket::kMediaProtocolSavpf); - std::vector cryptos; - cryptos.push_back(crypto_params); - media->set_cryptos(cryptos); + media->set_crypto(crypto_params); } } @@ -550,18 +546,15 @@ CreateSessionDescriptionForGroupCall(bool local, // Use SRTP master key material instead cricket::CryptoParams crypto_params; - crypto_params.crypto_suite = rtc::SrtpCryptoSuiteToName(srtp_key.suite); - std::string key(srtp_key.key_borrowed, srtp_key.key_len); - std::string salt(srtp_key.salt_borrowed, srtp_key.salt_len); - crypto_params.key_params = "inline:" + rtc::Base64::Encode(key + salt); + crypto_params.crypto_suite = srtp_key.suite; + crypto_params.key_params.SetData(srtp_key.key_borrowed, srtp_key.key_len); + crypto_params.key_params.AppendData(srtp_key.salt_borrowed, srtp_key.salt_len); auto set_rtp_params = [crypto_params] (cricket::MediaContentDescription* media) { media->set_protocol(cricket::kMediaProtocolSavpf); media->set_rtcp_mux(true); - std::vector cryptos; - cryptos.push_back(crypto_params); - media->set_cryptos(cryptos); + media->set_crypto(crypto_params); }; auto local_direction = local ? RtpTransceiverDirection::kSendOnly : RtpTransceiverDirection::kRecvOnly;