diff --git a/BUILD.gn b/BUILD.gn index 1fcc310669..d4e89fde83 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -619,6 +619,7 @@ if (rtc_include_tests) { "sdk/android/tests/src/org/webrtc/GlGenericDrawerTest.java", "sdk/android/tests/src/org/webrtc/HardwareVideoEncoderTest.java", "sdk/android/tests/src/org/webrtc/ScalingSettingsTest.java", + "sdk/android/tests/src/org/webrtc/CryptoOptionsTest.java", ] deps = [ diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h index 021be7d366..6504f534e3 100644 --- a/api/peerconnectioninterface.h +++ b/api/peerconnectioninterface.h @@ -406,6 +406,7 @@ class PeerConnectionInterface : public rtc::RefCountInterface { // Use new combined audio/video bandwidth estimation? absl::optional combined_audio_video_bwe; + // TODO(bugs.webrtc.org/9891) - Move to crypto_options // Can be used to disable DTLS-SRTP. This should never be done, but can be // useful for testing purposes, for example in setting up a loopback call // with a single PeerConnection. @@ -568,6 +569,7 @@ class PeerConnectionInterface : public rtc::RefCountInterface { // For all other users, specify kUnifiedPlan. SdpSemantics sdp_semantics = SdpSemantics::kPlanB; + // TODO(bugs.webrtc.org/9891) - Move to crypto_options or remove. // Actively reset the SRTP parameters whenever the DTLS transports // underneath are reset for every offer/answer negotiation. // This is only intended to be a workaround for crbug.com/835958 @@ -581,6 +583,11 @@ class PeerConnectionInterface : public rtc::RefCountInterface { // provided. bool use_media_transport = false; + // Defines advanced optional cryptographic settings related to SRTP and + // frame encryption for native WebRTC. Setting this will overwrite any + // settings set in PeerConnectionFactory (which is deprecated). + absl::optional crypto_options; + // // Don't forget to update operator== if adding something. // diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index 9dc5e663dc..08766445a9 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -706,6 +706,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( absl::optional network_preference; bool active_reset_srtp_params; bool use_media_transport; + absl::optional crypto_options; }; static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this), "Did you add something to RTCConfiguration and forget to " @@ -754,7 +755,8 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( sdp_semantics == o.sdp_semantics && network_preference == o.network_preference && active_reset_srtp_params == o.active_reset_srtp_params && - use_media_transport == o.use_media_transport; + use_media_transport == o.use_media_transport && + crypto_options == o.crypto_options; } bool PeerConnectionInterface::RTCConfiguration::operator!=( @@ -932,7 +934,11 @@ bool PeerConnection::Initialize( config.disable_encryption = options.disable_encryption; config.bundle_policy = configuration.bundle_policy; config.rtcp_mux_policy = configuration.rtcp_mux_policy; - config.crypto_options = options.crypto_options; + // TODO(bugs.webrtc.org/9891) - Remove options.crypto_options then remove this + // stub. + config.crypto_options = configuration.crypto_options.has_value() + ? *configuration.crypto_options + : options.crypto_options; config.transport_observer = this; config.event_log = event_log_.get(); #if defined(ENABLE_EXTERNAL_AUTH) @@ -1043,7 +1049,7 @@ bool PeerConnection::Initialize( } webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions( - options.crypto_options.srtp.enable_encrypted_rtp_header_extensions); + GetCryptoOptions().srtp.enable_encrypted_rtp_header_extensions); // Add default audio/video transceivers for Plan B SDP. if (!IsUnifiedPlan()) { @@ -2913,6 +2919,13 @@ bool PeerConnection::SetConfiguration(const RTCConfiguration& configuration, return SafeSetError(RTCErrorType::INVALID_MODIFICATION, error); } + if (local_description() && + configuration.crypto_options != configuration_.crypto_options) { + RTC_LOG(LS_ERROR) << "Can't change crypto_options after calling " + "SetLocalDescription."; + return SafeSetError(RTCErrorType::INVALID_MODIFICATION, error); + } + // The simplest (and most future-compatible) way to tell if the config was // modified in an invalid way is to copy each property we do support // modifying, then use operator==. There are far more properties we don't @@ -3715,7 +3728,7 @@ void PeerConnection::GetOptionsForOffer( } session_options->rtcp_cname = rtcp_cname_; - session_options->crypto_options = factory_->options().crypto_options; + session_options->crypto_options = GetCryptoOptions(); session_options->is_unified_plan = IsUnifiedPlan(); session_options->pooled_ice_credentials = network_thread()->Invoke>( @@ -3980,7 +3993,7 @@ void PeerConnection::GetOptionsForAnswer( } session_options->rtcp_cname = rtcp_cname_; - session_options->crypto_options = factory_->options().crypto_options; + session_options->crypto_options = GetCryptoOptions(); session_options->is_unified_plan = IsUnifiedPlan(); session_options->pooled_ice_credentials = network_thread()->Invoke>( @@ -5588,8 +5601,8 @@ cricket::VoiceChannel* PeerConnection::CreateVoiceChannel( cricket::VoiceChannel* voice_channel = channel_manager()->CreateVoiceChannel( call_.get(), configuration_.media_config, rtp_transport, media_transport, - signaling_thread(), mid, SrtpRequired(), - factory_->options().crypto_options, audio_options_); + signaling_thread(), mid, SrtpRequired(), GetCryptoOptions(), + audio_options_); if (!voice_channel) { return nullptr; } @@ -5610,8 +5623,8 @@ cricket::VideoChannel* PeerConnection::CreateVideoChannel( // TODO(sukhanov): Propagate media_transport to video channel. cricket::VideoChannel* video_channel = channel_manager()->CreateVideoChannel( call_.get(), configuration_.media_config, rtp_transport, - signaling_thread(), mid, SrtpRequired(), - factory_->options().crypto_options, video_options_); + signaling_thread(), mid, SrtpRequired(), GetCryptoOptions(), + video_options_); if (!video_channel) { return nullptr; } @@ -5645,7 +5658,7 @@ bool PeerConnection::CreateDataChannel(const std::string& mid) { RtpTransportInternal* rtp_transport = GetRtpTransport(mid); rtp_data_channel_ = channel_manager()->CreateRtpDataChannel( configuration_.media_config, rtp_transport, signaling_thread(), mid, - SrtpRequired(), factory_->options().crypto_options); + SrtpRequired(), GetCryptoOptions()); if (!rtp_data_channel_) { return false; } @@ -6343,6 +6356,14 @@ PeerConnectionObserver* PeerConnection::Observer() const { return observer_; } +CryptoOptions PeerConnection::GetCryptoOptions() { + // TODO(bugs.webrtc.org/9891) - Remove PeerConnectionFactory::CryptoOptions + // after it has been removed. + return configuration_.crypto_options.has_value() + ? *configuration_.crypto_options + : factory_->options().crypto_options; +} + void PeerConnection::ClearStatsCache() { if (stats_collector_) { stats_collector_->ClearCachedStatsReport(); diff --git a/pc/peerconnection.h b/pc/peerconnection.h index 604a5d3793..e725798aca 100644 --- a/pc/peerconnection.h +++ b/pc/peerconnection.h @@ -922,6 +922,11 @@ class PeerConnection : public PeerConnectionInternal, // Returns the observer. Will crash on CHECK if the observer is removed. PeerConnectionObserver* Observer() const; + // Returns the CryptoOptions for this PeerConnection. This will always + // return the RTCConfiguration.crypto_options if set and will only default + // back to the PeerConnectionFactory settings if nothing was set. + CryptoOptions GetCryptoOptions(); + // Returns rtp transport, result can not be nullptr. RtpTransportInternal* GetRtpTransport(const std::string& mid) { auto rtp_transport = transport_controller_->GetRtpTransport(mid); diff --git a/pc/peerconnection_crypto_unittest.cc b/pc/peerconnection_crypto_unittest.cc index 1a53d860bf..d61c41c009 100644 --- a/pc/peerconnection_crypto_unittest.cc +++ b/pc/peerconnection_crypto_unittest.cc @@ -278,6 +278,28 @@ TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) { answer->description())); } +// CryptoOptions has been promoted to RTCConfiguration. As such if it is ever +// set in the configuration it should overrite the settings set in the factory. +TEST_P(PeerConnectionCryptoTest, RTCConfigurationCryptoOptionOverridesFactory) { + PeerConnectionFactoryInterface::Options options; + options.crypto_options.srtp.enable_gcm_crypto_suites = true; + pc_factory_->SetOptions(options); + + RTCConfiguration config; + config.enable_dtls_srtp.emplace(false); + CryptoOptions crypto_options; + crypto_options.srtp.enable_gcm_crypto_suites = false; + config.crypto_options = crypto_options; + auto caller = CreatePeerConnectionWithAudioVideo(config); + + auto offer = caller->CreateOffer(); + ASSERT_TRUE(offer); + + ASSERT_FALSE(offer->description()->contents().empty()); + // This should exist if GCM is enabled see CorrectCryptoInOfferWithSdesAndGcm + EXPECT_FALSE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description())); +} + // When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer // should have the correct ciphers in the SDES crypto options. // With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1 @@ -297,6 +319,7 @@ TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWithSdesAndGcm) { ASSERT_FALSE(offer->description()->contents().empty()); EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description())); } + TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWithSdesAndGcm) { PeerConnectionFactoryInterface::Options options; options.crypto_options.srtp.enable_gcm_crypto_suites = true; diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index 03bdf3bd2a..a8a1b03d50 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -797,6 +797,8 @@ if (is_ios || is_mac) { "objc/api/peerconnection/RTCConfiguration+Private.h", "objc/api/peerconnection/RTCConfiguration.h", "objc/api/peerconnection/RTCConfiguration.mm", + "objc/api/peerconnection/RTCCryptoOptions.h", + "objc/api/peerconnection/RTCCryptoOptions.mm", "objc/api/peerconnection/RTCDataChannel+Private.h", "objc/api/peerconnection/RTCDataChannel.h", "objc/api/peerconnection/RTCDataChannel.mm", @@ -1274,6 +1276,7 @@ if (is_ios || is_mac) { "objc/api/peerconnection/RTCSessionDescription.h", "objc/api/peerconnection/RTCTracing.h", "objc/api/peerconnection/RTCCertificate.h", + "objc/api/peerconnection/RTCCryptoOptions.h", "objc/api/peerconnection/RTCVideoSource.h", "objc/api/peerconnection/RTCVideoTrack.h", "objc/api/video_codec/RTCVideoCodecConstants.h", @@ -1362,6 +1365,7 @@ if (is_ios || is_mac) { "objc/api/peerconnection/RTCAudioTrack.h", "objc/api/peerconnection/RTCCertificate.h", "objc/api/peerconnection/RTCConfiguration.h", + "objc/api/peerconnection/RTCCryptoOptions.h", "objc/api/peerconnection/RTCDataChannel.h", "objc/api/peerconnection/RTCDataChannelConfiguration.h", "objc/api/peerconnection/RTCDtmfSender.h", diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn index 68cd77a669..8a3f8ccd25 100644 --- a/sdk/android/BUILD.gn +++ b/sdk/android/BUILD.gn @@ -276,6 +276,7 @@ if (is_android) { "api/org/webrtc/AudioSource.java", "api/org/webrtc/AudioTrack.java", "api/org/webrtc/CallSessionFileRotatingLogSink.java", + "api/org/webrtc/CryptoOptions.java", "api/org/webrtc/DataChannel.java", "api/org/webrtc/DtmfSender.java", "api/org/webrtc/FecControllerFactoryFactoryInterface.java", @@ -609,6 +610,8 @@ if (is_android) { "src/jni/pc/androidnetworkmonitor.h", "src/jni/pc/audiotrack.cc", "src/jni/pc/callsessionfilerotatinglogsink.cc", + "src/jni/pc/cryptooptions.cc", + "src/jni/pc/cryptooptions.h", "src/jni/pc/datachannel.cc", "src/jni/pc/datachannel.h", "src/jni/pc/dtmfsender.cc", @@ -674,6 +677,7 @@ if (is_android) { "../../rtc_base:stringutils", "../../system_wrappers:field_trial", "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/types:optional", ] } @@ -1173,6 +1177,7 @@ if (is_android) { sources = [ "api/org/webrtc/AudioTrack.java", "api/org/webrtc/CallSessionFileRotatingLogSink.java", + "api/org/webrtc/CryptoOptions.java", "api/org/webrtc/DataChannel.java", "api/org/webrtc/DtmfSender.java", "api/org/webrtc/IceCandidate.java", diff --git a/sdk/android/api/org/webrtc/CryptoOptions.java b/sdk/android/api/org/webrtc/CryptoOptions.java new file mode 100644 index 0000000000..1ccc6df71d --- /dev/null +++ b/sdk/android/api/org/webrtc/CryptoOptions.java @@ -0,0 +1,144 @@ +/* + * Copyright 2018 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. + */ +package org.webrtc; + +/** + * CryptoOptions defines advanced cryptographic settings for native WebRTC. + * These settings must be passed into RTCConfiguration. WebRTC is secur by + * default and you should not need to set any of these options unless you are + * specifically looking for an additional crypto feature such as AES_GCM + * support. This class is the Java binding of native api/crypto/cryptooptions.h + */ +public final class CryptoOptions { + /** + * SRTP Related Peer Connection Options. + */ + public final class Srtp { + /** + * Enable GCM crypto suites from RFC 7714 for SRTP. GCM will only be used + * if both sides enable it + */ + private final boolean enableGcmCryptoSuites; + /** + * If set to true, the (potentially insecure) crypto cipher + * SRTP_AES128_CM_SHA1_32 will be included in the list of supported ciphers + * during negotiation. It will only be used if both peers support it and no + * other ciphers get preferred. + */ + private final boolean enableAes128Sha1_32CryptoCipher; + /** + * If set to true, encrypted RTP header extensions as defined in RFC 6904 + * will be negotiated. They will only be used if both peers support them. + */ + private final boolean enableEncryptedRtpHeaderExtensions; + + private Srtp(boolean enableGcmCryptoSuites, boolean enableAes128Sha1_32CryptoCipher, + boolean enableEncryptedRtpHeaderExtensions) { + this.enableGcmCryptoSuites = enableGcmCryptoSuites; + this.enableAes128Sha1_32CryptoCipher = enableAes128Sha1_32CryptoCipher; + this.enableEncryptedRtpHeaderExtensions = enableEncryptedRtpHeaderExtensions; + } + + @CalledByNative("Srtp") + public boolean getEnableGcmCryptoSuites() { + return enableGcmCryptoSuites; + } + + @CalledByNative("Srtp") + public boolean getEnableAes128Sha1_32CryptoCipher() { + return enableAes128Sha1_32CryptoCipher; + } + + @CalledByNative("Srtp") + public boolean getEnableEncryptedRtpHeaderExtensions() { + return enableEncryptedRtpHeaderExtensions; + } + } + + /** + * Options to be used when the FrameEncryptor / FrameDecryptor APIs are used. + */ + public final class SFrame { + /** + * If set all RtpSenders must have an FrameEncryptor attached to them before + * they are allowed to send packets. All RtpReceivers must have a + * FrameDecryptor attached to them before they are able to receive packets. + */ + private final boolean requireFrameEncryption; + + private SFrame(boolean requireFrameEncryption) { + this.requireFrameEncryption = requireFrameEncryption; + } + + @CalledByNative("SFrame") + public boolean getRequireFrameEncryption() { + return requireFrameEncryption; + } + } + + private final Srtp srtp; + private final SFrame sframe; + + private CryptoOptions(boolean enableGcmCryptoSuites, boolean enableAes128Sha1_32CryptoCipher, + boolean enableEncryptedRtpHeaderExtensions, boolean requireFrameEncryption) { + this.srtp = new Srtp( + enableGcmCryptoSuites, enableAes128Sha1_32CryptoCipher, enableEncryptedRtpHeaderExtensions); + this.sframe = new SFrame(requireFrameEncryption); + } + + public static Builder builder() { + return new Builder(); + } + + @CalledByNative + public Srtp getSrtp() { + return srtp; + } + + @CalledByNative + public SFrame getSFrame() { + return sframe; + } + + public static class Builder { + private boolean enableGcmCryptoSuites = false; + private boolean enableAes128Sha1_32CryptoCipher = false; + private boolean enableEncryptedRtpHeaderExtensions = false; + private boolean requireFrameEncryption = false; + + private Builder() {} + + public Builder setEnableGcmCryptoSuites(boolean enableGcmCryptoSuites) { + this.enableGcmCryptoSuites = enableGcmCryptoSuites; + return this; + } + + public Builder setEnableAes128Sha1_32CryptoCipher(boolean enableAes128Sha1_32CryptoCipher) { + this.enableAes128Sha1_32CryptoCipher = enableAes128Sha1_32CryptoCipher; + return this; + } + + public Builder setEnableEncryptedRtpHeaderExtensions( + boolean enableEncryptedRtpHeaderExtensions) { + this.enableEncryptedRtpHeaderExtensions = enableEncryptedRtpHeaderExtensions; + return this; + } + + public Builder setRequireFrameEncryption(boolean requireFrameEncryption) { + this.requireFrameEncryption = requireFrameEncryption; + return this; + } + + public CryptoOptions createCryptoOptions() { + return new CryptoOptions(enableGcmCryptoSuites, enableAes128Sha1_32CryptoCipher, + enableEncryptedRtpHeaderExtensions, requireFrameEncryption); + } + } +} diff --git a/sdk/android/api/org/webrtc/PeerConnection.java b/sdk/android/api/org/webrtc/PeerConnection.java index d50819c87b..1cb5717b4e 100644 --- a/sdk/android/api/org/webrtc/PeerConnection.java +++ b/sdk/android/api/org/webrtc/PeerConnection.java @@ -468,6 +468,13 @@ public class PeerConnection { */ public boolean useMediaTransport; + /** + * Defines advanced optional cryptographic settings related to SRTP and + * frame encryption for native WebRTC. Setting this will overwrite any + * options set through the PeerConnectionFactory (which is deprecated). + */ + @Nullable public CryptoOptions cryptoOptions; + // TODO(deadbeef): Instead of duplicating the defaults here, we should do // something to pick up the defaults from C++. The Objective-C equivalent // of RTCConfiguration does that. @@ -508,6 +515,7 @@ public class PeerConnection { sdpSemantics = SdpSemantics.PLAN_B; activeResetSrtpParams = false; useMediaTransport = false; + cryptoOptions = null; } @CalledByNative("RTCConfiguration") @@ -711,6 +719,12 @@ public class PeerConnection { boolean getUseMediaTransport() { return useMediaTransport; } + + @Nullable + @CalledByNative("RTCConfiguration") + CryptoOptions getCryptoOptions() { + return cryptoOptions; + } }; private final List localStreams = new ArrayList<>(); diff --git a/sdk/android/api/org/webrtc/PeerConnectionFactory.java b/sdk/android/api/org/webrtc/PeerConnectionFactory.java index fdc29bbae9..5e2289cb8c 100644 --- a/sdk/android/api/org/webrtc/PeerConnectionFactory.java +++ b/sdk/android/api/org/webrtc/PeerConnectionFactory.java @@ -123,6 +123,9 @@ public class PeerConnectionFactory { public boolean disableEncryption; public boolean disableNetworkMonitor; + // TODO(webrtc:9891) - The below crypto options are deprecated. Please use + // RTCConfiguration to set these options instead. They will be removed in + // a future release. /** * If set to true, the (potentially insecure) crypto cipher SRTP_AES128_CM_SHA1_32 * will be included in the list of supported ciphers during negotiation. It will only @@ -158,16 +161,19 @@ public class PeerConnectionFactory { return disableNetworkMonitor; } + @Deprecated @CalledByNative("Options") boolean getEnableAes128Sha1_32CryptoCipher() { return enableAes128Sha1_32CryptoCipher; } + @Deprecated @CalledByNative("Options") boolean getEnableGcmCryptoSuites() { return enableGcmCryptoSuites; } + @Deprecated @CalledByNative("Options") boolean getRequireFrameEncryption() { return requireFrameEncryption; diff --git a/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java b/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java index 894e2bde3c..cd24037daf 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java @@ -682,6 +682,27 @@ public class PeerConnectionTest { assertEquals(originalCert.certificate, restoredCert.certificate); } + @Test + @SmallTest + public void testCreationWithCryptoOptions() throws Exception { + PeerConnectionFactory factory = PeerConnectionFactory.builder().createPeerConnectionFactory(); + PeerConnection.RTCConfiguration config = new PeerConnection.RTCConfiguration(Arrays.asList()); + + assertNull(config.cryptoOptions); + + CryptoOptions cryptoOptions = CryptoOptions.builder() + .setEnableGcmCryptoSuites(true) + .setEnableAes128Sha1_32CryptoCipher(true) + .setEnableEncryptedRtpHeaderExtensions(true) + .setRequireFrameEncryption(true) + .createCryptoOptions(); + config.cryptoOptions = cryptoOptions; + + ObserverExpectations offeringExpectations = new ObserverExpectations("PCTest:offerer"); + PeerConnection offeringPC = factory.createPeerConnection(config, offeringExpectations); + assertNotNull(offeringPC); + } + @Test @MediumTest public void testCompleteSession() throws Exception { diff --git a/sdk/android/src/jni/pc/cryptooptions.cc b/sdk/android/src/jni/pc/cryptooptions.cc new file mode 100644 index 0000000000..0f38b13f78 --- /dev/null +++ b/sdk/android/src/jni/pc/cryptooptions.cc @@ -0,0 +1,43 @@ +/* + * Copyright 2018 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 "sdk/android/src/jni/pc/cryptooptions.h" + +#include "sdk/android/generated_peerconnection_jni/jni/CryptoOptions_jni.h" + +namespace webrtc { +namespace jni { + +absl::optional JavaToNativeOptionalCryptoOptions( + JNIEnv* jni, + const JavaRef& j_crypto_options) { + if (j_crypto_options.is_null()) { + return absl::nullopt; + } + + ScopedJavaLocalRef j_srtp = + Java_CryptoOptions_getSrtp(jni, j_crypto_options); + ScopedJavaLocalRef j_sframe = + Java_CryptoOptions_getSFrame(jni, j_crypto_options); + + CryptoOptions native_crypto_options; + native_crypto_options.srtp.enable_gcm_crypto_suites = + Java_Srtp_getEnableGcmCryptoSuites(jni, j_srtp); + native_crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher = + Java_Srtp_getEnableAes128Sha1_32CryptoCipher(jni, j_srtp); + native_crypto_options.srtp.enable_encrypted_rtp_header_extensions = + Java_Srtp_getEnableEncryptedRtpHeaderExtensions(jni, j_srtp); + native_crypto_options.sframe.require_frame_encryption = + Java_SFrame_getRequireFrameEncryption(jni, j_sframe); + return absl::optional(native_crypto_options); +} + +} // namespace jni +} // namespace webrtc diff --git a/sdk/android/src/jni/pc/cryptooptions.h b/sdk/android/src/jni/pc/cryptooptions.h new file mode 100644 index 0000000000..9890264a55 --- /dev/null +++ b/sdk/android/src/jni/pc/cryptooptions.h @@ -0,0 +1,30 @@ +/* + * Copyright 2018 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. + */ + +#ifndef SDK_ANDROID_SRC_JNI_PC_CRYPTOOPTIONS_H_ +#define SDK_ANDROID_SRC_JNI_PC_CRYPTOOPTIONS_H_ + +#include + +#include "absl/types/optional.h" +#include "api/crypto/cryptooptions.h" +#include "sdk/android/native_api/jni/scoped_java_ref.h" + +namespace webrtc { +namespace jni { + +absl::optional JavaToNativeOptionalCryptoOptions( + JNIEnv* jni, + const JavaRef& j_crypto_options); + +} // namespace jni +} // namespace webrtc + +#endif // SDK_ANDROID_SRC_JNI_PC_CRYPTOOPTIONS_H_ diff --git a/sdk/android/src/jni/pc/peerconnection.cc b/sdk/android/src/jni/pc/peerconnection.cc index 8a29199cf3..0d51847575 100644 --- a/sdk/android/src/jni/pc/peerconnection.cc +++ b/sdk/android/src/jni/pc/peerconnection.cc @@ -43,6 +43,7 @@ #include "sdk/android/generated_peerconnection_jni/jni/PeerConnection_jni.h" #include "sdk/android/native_api/jni/java_types.h" #include "sdk/android/src/jni/jni_helpers.h" +#include "sdk/android/src/jni/pc/cryptooptions.h" #include "sdk/android/src/jni/pc/datachannel.h" #include "sdk/android/src/jni/pc/icecandidate.h" #include "sdk/android/src/jni/pc/mediaconstraints.h" @@ -146,6 +147,8 @@ void JavaToNativeRTCConfiguration( Java_RTCConfiguration_getNetworkPreference(jni, j_rtc_config); ScopedJavaLocalRef j_sdp_semantics = Java_RTCConfiguration_getSdpSemantics(jni, j_rtc_config); + ScopedJavaLocalRef j_crypto_options = + Java_RTCConfiguration_getCryptoOptions(jni, j_rtc_config); rtc_config->type = JavaToNativeIceTransportsType(jni, j_ice_transports_type); rtc_config->bundle_policy = JavaToNativeBundlePolicy(jni, j_bundle_policy); @@ -246,6 +249,8 @@ void JavaToNativeRTCConfiguration( Java_RTCConfiguration_getActiveResetSrtpParams(jni, j_rtc_config); rtc_config->use_media_transport = Java_RTCConfiguration_getUseMediaTransport(jni, j_rtc_config); + rtc_config->crypto_options = + JavaToNativeOptionalCryptoOptions(jni, j_crypto_options); } rtc::KeyType GetRtcConfigKeyType(JNIEnv* env, diff --git a/sdk/android/tests/src/org/webrtc/CryptoOptionsTest.java b/sdk/android/tests/src/org/webrtc/CryptoOptionsTest.java new file mode 100644 index 0000000000..f03811e6fc --- /dev/null +++ b/sdk/android/tests/src/org/webrtc/CryptoOptionsTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2018 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. + */ + +package org.webrtc; + +import static com.google.common.truth.Truth.assertThat; + +import org.chromium.testing.local.LocalRobolectricTestRunner; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; +import org.webrtc.CryptoOptions; + +@RunWith(LocalRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class CryptoOptionsTest { + // Validates the builder builds by default all false options. + @Test + public void testBuilderDefaultsAreFalse() { + CryptoOptions cryptoOptions = CryptoOptions.builder().createCryptoOptions(); + assertThat(cryptoOptions.getSrtp().getEnableGcmCryptoSuites()).isFalse(); + assertThat(cryptoOptions.getSrtp().getEnableAes128Sha1_32CryptoCipher()).isFalse(); + assertThat(cryptoOptions.getSrtp().getEnableEncryptedRtpHeaderExtensions()).isFalse(); + assertThat(cryptoOptions.getSFrame().getRequireFrameEncryption()).isFalse(); + } + + // Validates the builder sets the correct parameters. + @Test + public void testBuilderCorrectlyInitializingGcmCrypto() { + CryptoOptions cryptoOptions = + CryptoOptions.builder().setEnableGcmCryptoSuites(true).createCryptoOptions(); + assertThat(cryptoOptions.getSrtp().getEnableGcmCryptoSuites()).isTrue(); + assertThat(cryptoOptions.getSrtp().getEnableAes128Sha1_32CryptoCipher()).isFalse(); + assertThat(cryptoOptions.getSrtp().getEnableEncryptedRtpHeaderExtensions()).isFalse(); + assertThat(cryptoOptions.getSFrame().getRequireFrameEncryption()).isFalse(); + } + + @Test + public void testBuilderCorrectlyInitializingAes128Sha1_32CryptoCipher() { + CryptoOptions cryptoOptions = + CryptoOptions.builder().setEnableAes128Sha1_32CryptoCipher(true).createCryptoOptions(); + assertThat(cryptoOptions.getSrtp().getEnableGcmCryptoSuites()).isFalse(); + assertThat(cryptoOptions.getSrtp().getEnableAes128Sha1_32CryptoCipher()).isTrue(); + assertThat(cryptoOptions.getSrtp().getEnableEncryptedRtpHeaderExtensions()).isFalse(); + assertThat(cryptoOptions.getSFrame().getRequireFrameEncryption()).isFalse(); + } + + @Test + public void testBuilderCorrectlyInitializingEncryptedRtpHeaderExtensions() { + CryptoOptions cryptoOptions = + CryptoOptions.builder().setEnableEncryptedRtpHeaderExtensions(true).createCryptoOptions(); + assertThat(cryptoOptions.getSrtp().getEnableGcmCryptoSuites()).isFalse(); + assertThat(cryptoOptions.getSrtp().getEnableAes128Sha1_32CryptoCipher()).isFalse(); + assertThat(cryptoOptions.getSrtp().getEnableEncryptedRtpHeaderExtensions()).isTrue(); + assertThat(cryptoOptions.getSFrame().getRequireFrameEncryption()).isFalse(); + } + + @Test + public void testBuilderCorrectlyInitializingRequireFrameEncryption() { + CryptoOptions cryptoOptions = + CryptoOptions.builder().setRequireFrameEncryption(true).createCryptoOptions(); + assertThat(cryptoOptions.getSrtp().getEnableGcmCryptoSuites()).isFalse(); + assertThat(cryptoOptions.getSrtp().getEnableAes128Sha1_32CryptoCipher()).isFalse(); + assertThat(cryptoOptions.getSrtp().getEnableEncryptedRtpHeaderExtensions()).isFalse(); + assertThat(cryptoOptions.getSFrame().getRequireFrameEncryption()).isTrue(); + } +} diff --git a/sdk/objc/api/peerconnection/RTCConfiguration.h b/sdk/objc/api/peerconnection/RTCConfiguration.h index c7998a5aa2..9f41fd28d2 100644 --- a/sdk/objc/api/peerconnection/RTCConfiguration.h +++ b/sdk/objc/api/peerconnection/RTCConfiguration.h @@ -11,6 +11,7 @@ #import #import "RTCCertificate.h" +#import "RTCCryptoOptions.h" #import "RTCMacros.h" @class RTCIceServer; @@ -174,6 +175,13 @@ RTC_OBJC_EXPORT */ @property(nonatomic, assign) BOOL useMediaTransport; +/** + * Defines advanced optional cryptographic settings related to SRTP and + * frame encryption for native WebRTC. Setting this will overwrite any + * options set through the PeerConnectionFactory (which is deprecated). + */ +@property(nonatomic, nullable) RTCCryptoOptions *cryptoOptions; + - (instancetype)init; @end diff --git a/sdk/objc/api/peerconnection/RTCConfiguration.mm b/sdk/objc/api/peerconnection/RTCConfiguration.mm index bdf4fb6e43..35cdc466e9 100644 --- a/sdk/objc/api/peerconnection/RTCConfiguration.mm +++ b/sdk/objc/api/peerconnection/RTCConfiguration.mm @@ -49,6 +49,7 @@ @synthesize turnCustomizer = _turnCustomizer; @synthesize activeResetSrtpParams = _activeResetSrtpParams; @synthesize useMediaTransport = _useMediaTransport; +@synthesize cryptoOptions = _cryptoOptions; - (instancetype)init { // Copy defaults. @@ -111,6 +112,17 @@ _sdpSemantics = [[self class] sdpSemanticsForNativeSdpSemantics:config.sdp_semantics]; _turnCustomizer = config.turn_customizer; _activeResetSrtpParams = config.active_reset_srtp_params; + if (config.crypto_options) { + _cryptoOptions = [[RTCCryptoOptions alloc] + initWithSrtpEnableGcmCryptoSuites:config.crypto_options->srtp + .enable_gcm_crypto_suites + srtpEnableAes128Sha1_32CryptoCipher:config.crypto_options->srtp + .enable_aes128_sha1_32_crypto_cipher + srtpEnableEncryptedRtpHeaderExtensions:config.crypto_options->srtp + .enable_encrypted_rtp_header_extensions + sframeRequireFrameEncryption:config.crypto_options->sframe + .require_frame_encryption]; + } } return self; } @@ -224,6 +236,19 @@ nativeConfig->turn_customizer = _turnCustomizer; } nativeConfig->active_reset_srtp_params = _activeResetSrtpParams ? true : false; + if (_cryptoOptions) { + webrtc::CryptoOptions nativeCryptoOptions; + nativeCryptoOptions.srtp.enable_gcm_crypto_suites = + _cryptoOptions.srtpEnableGcmCryptoSuites ? true : false; + nativeCryptoOptions.srtp.enable_aes128_sha1_32_crypto_cipher = + _cryptoOptions.srtpEnableAes128Sha1_32CryptoCipher ? true : false; + nativeCryptoOptions.srtp.enable_encrypted_rtp_header_extensions = + _cryptoOptions.srtpEnableEncryptedRtpHeaderExtensions ? true : false; + nativeCryptoOptions.sframe.require_frame_encryption = + _cryptoOptions.sframeRequireFrameEncryption ? true : false; + nativeConfig->crypto_options = absl::optional(nativeCryptoOptions); + } + return nativeConfig.release(); } diff --git a/sdk/objc/api/peerconnection/RTCCryptoOptions.h b/sdk/objc/api/peerconnection/RTCCryptoOptions.h new file mode 100644 index 0000000000..b465bb5a73 --- /dev/null +++ b/sdk/objc/api/peerconnection/RTCCryptoOptions.h @@ -0,0 +1,63 @@ +/* + * Copyright 2018 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. + */ + +#import + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Objective-C bindings for webrtc::CryptoOptions. This API had to be flattened + * as Objective-C doesn't support nested structures. + */ +RTC_OBJC_EXPORT +@interface RTCCryptoOptions : NSObject + +/** + * Enable GCM crypto suites from RFC 7714 for SRTP. GCM will only be used + * if both sides enable it + */ +@property(nonatomic, assign) BOOL srtpEnableGcmCryptoSuites; +/** + * If set to true, the (potentially insecure) crypto cipher + * SRTP_AES128_CM_SHA1_32 will be included in the list of supported ciphers + * during negotiation. It will only be used if both peers support it and no + * other ciphers get preferred. + */ +@property(nonatomic, assign) BOOL srtpEnableAes128Sha1_32CryptoCipher; +/** + * If set to true, encrypted RTP header extensions as defined in RFC 6904 + * will be negotiated. They will only be used if both peers support them. + */ +@property(nonatomic, assign) BOOL srtpEnableEncryptedRtpHeaderExtensions; + +/** + * If set all RtpSenders must have an FrameEncryptor attached to them before + * they are allowed to send packets. All RtpReceivers must have a + * FrameDecryptor attached to them before they are able to receive packets. + */ +@property(nonatomic, assign) BOOL sframeRequireFrameEncryption; + +/** + * Initializes CryptoOptions with all possible options set explicitly. This + * is done when converting from a native RTCConfiguration.crypto_options. + */ +- (instancetype)initWithSrtpEnableGcmCryptoSuites:(BOOL)srtpEnableGcmCryptoSuites + srtpEnableAes128Sha1_32CryptoCipher:(BOOL)srtpEnableAes128Sha1_32CryptoCipher + srtpEnableEncryptedRtpHeaderExtensions:(BOOL)srtpEnableEncryptedRtpHeaderExtensions + sframeRequireFrameEncryption:(BOOL)sframeRequireFrameEncryption + NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk/objc/api/peerconnection/RTCCryptoOptions.mm b/sdk/objc/api/peerconnection/RTCCryptoOptions.mm new file mode 100644 index 0000000000..a059f75599 --- /dev/null +++ b/sdk/objc/api/peerconnection/RTCCryptoOptions.mm @@ -0,0 +1,33 @@ +/* + * Copyright 2018 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. + */ + +#import "RTCCryptoOptions.h" + +@implementation RTCCryptoOptions + +@synthesize srtpEnableGcmCryptoSuites = _srtpEnableGcmCryptoSuites; +@synthesize srtpEnableAes128Sha1_32CryptoCipher = _srtpEnableAes128Sha1_32CryptoCipher; +@synthesize srtpEnableEncryptedRtpHeaderExtensions = _srtpEnableEncryptedRtpHeaderExtensions; +@synthesize sframeRequireFrameEncryption = _sframeRequireFrameEncryption; + +- (instancetype)initWithSrtpEnableGcmCryptoSuites:(BOOL)srtpEnableGcmCryptoSuites + srtpEnableAes128Sha1_32CryptoCipher:(BOOL)srtpEnableAes128Sha1_32CryptoCipher + srtpEnableEncryptedRtpHeaderExtensions:(BOOL)srtpEnableEncryptedRtpHeaderExtensions + sframeRequireFrameEncryption:(BOOL)sframeRequireFrameEncryption { + if (self = [super init]) { + _srtpEnableGcmCryptoSuites = srtpEnableGcmCryptoSuites; + _srtpEnableAes128Sha1_32CryptoCipher = srtpEnableAes128Sha1_32CryptoCipher; + _srtpEnableEncryptedRtpHeaderExtensions = srtpEnableEncryptedRtpHeaderExtensions; + _sframeRequireFrameEncryption = sframeRequireFrameEncryption; + } + return self; +} + +@end diff --git a/sdk/objc/unittests/RTCConfigurationTest.mm b/sdk/objc/unittests/RTCConfigurationTest.mm index 1ef718ad49..67a303e550 100644 --- a/sdk/objc/unittests/RTCConfigurationTest.mm +++ b/sdk/objc/unittests/RTCConfigurationTest.mm @@ -50,6 +50,10 @@ RTCContinualGatheringPolicyGatherContinually; config.shouldPruneTurnPorts = YES; config.iceRegatherIntervalRange = range; + config.cryptoOptions = [[RTCCryptoOptions alloc] initWithSrtpEnableGcmCryptoSuites:YES + srtpEnableAes128Sha1_32CryptoCipher:YES + srtpEnableEncryptedRtpHeaderExtensions:YES + sframeRequireFrameEncryption:YES]; std::unique_ptr nativeConfig([config createNativeConfiguration]); @@ -78,6 +82,10 @@ EXPECT_EQ(true, nativeConfig->prune_turn_ports); EXPECT_EQ(range.min, nativeConfig->ice_regather_interval_range->min()); EXPECT_EQ(range.max, nativeConfig->ice_regather_interval_range->max()); + EXPECT_EQ(true, nativeConfig->crypto_options->srtp.enable_gcm_crypto_suites); + EXPECT_EQ(true, nativeConfig->crypto_options->srtp.enable_aes128_sha1_32_crypto_cipher); + EXPECT_EQ(true, nativeConfig->crypto_options->srtp.enable_encrypted_rtp_header_extensions); + EXPECT_EQ(true, nativeConfig->crypto_options->sframe.require_frame_encryption); } - (void)testNativeConversionToConfiguration { @@ -103,6 +111,10 @@ RTCContinualGatheringPolicyGatherContinually; config.shouldPruneTurnPorts = YES; config.iceRegatherIntervalRange = range; + config.cryptoOptions = [[RTCCryptoOptions alloc] initWithSrtpEnableGcmCryptoSuites:YES + srtpEnableAes128Sha1_32CryptoCipher:NO + srtpEnableEncryptedRtpHeaderExtensions:NO + sframeRequireFrameEncryption:NO]; webrtc::PeerConnectionInterface::RTCConfiguration *nativeConfig = [config createNativeConfiguration]; @@ -130,6 +142,19 @@ EXPECT_EQ(config.shouldPruneTurnPorts, newConfig.shouldPruneTurnPorts); EXPECT_EQ(config.iceRegatherIntervalRange.min, newConfig.iceRegatherIntervalRange.min); EXPECT_EQ(config.iceRegatherIntervalRange.max, newConfig.iceRegatherIntervalRange.max); + EXPECT_EQ(config.cryptoOptions.srtpEnableGcmCryptoSuites, + newConfig.cryptoOptions.srtpEnableGcmCryptoSuites); + EXPECT_EQ(config.cryptoOptions.srtpEnableAes128Sha1_32CryptoCipher, + newConfig.cryptoOptions.srtpEnableAes128Sha1_32CryptoCipher); + EXPECT_EQ(config.cryptoOptions.srtpEnableEncryptedRtpHeaderExtensions, + newConfig.cryptoOptions.srtpEnableEncryptedRtpHeaderExtensions); + EXPECT_EQ(config.cryptoOptions.sframeRequireFrameEncryption, + newConfig.cryptoOptions.sframeRequireFrameEncryption); +} + +- (void)testDefaultValues { + RTCConfiguration *config = [[RTCConfiguration alloc] init]; + EXPECT_EQ(config.cryptoOptions, nil); } @end @@ -139,5 +164,6 @@ TEST(RTCConfigurationTest, NativeConfigurationConversionTest) { RTCConfigurationTest *test = [[RTCConfigurationTest alloc] init]; [test testConversionToNativeConfiguration]; [test testNativeConversionToConfiguration]; + [test testDefaultValues]; } } diff --git a/sdk/objc/unittests/RTCPeerConnectionTest.mm b/sdk/objc/unittests/RTCPeerConnectionTest.mm index 28fdee6a2b..3532258799 100644 --- a/sdk/objc/unittests/RTCPeerConnectionTest.mm +++ b/sdk/objc/unittests/RTCPeerConnectionTest.mm @@ -16,6 +16,7 @@ #import "api/peerconnection/RTCConfiguration+Private.h" #import "api/peerconnection/RTCConfiguration.h" +#import "api/peerconnection/RTCCryptoOptions.h" #import "api/peerconnection/RTCIceServer.h" #import "api/peerconnection/RTCMediaConstraints.h" #import "api/peerconnection/RTCPeerConnection.h" @@ -50,6 +51,10 @@ RTCContinualGatheringPolicyGatherContinually; config.shouldPruneTurnPorts = YES; config.activeResetSrtpParams = YES; + config.cryptoOptions = [[RTCCryptoOptions alloc] initWithSrtpEnableGcmCryptoSuites:YES + srtpEnableAes128Sha1_32CryptoCipher:YES + srtpEnableEncryptedRtpHeaderExtensions:NO + sframeRequireFrameEncryption:NO]; RTCMediaConstraints *contraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:@{} optionalConstraints:nil]; @@ -89,6 +94,14 @@ EXPECT_EQ(config.continualGatheringPolicy, newConfig.continualGatheringPolicy); EXPECT_EQ(config.shouldPruneTurnPorts, newConfig.shouldPruneTurnPorts); EXPECT_EQ(config.activeResetSrtpParams, newConfig.activeResetSrtpParams); + EXPECT_EQ(config.cryptoOptions.srtpEnableGcmCryptoSuites, + newConfig.cryptoOptions.srtpEnableGcmCryptoSuites); + EXPECT_EQ(config.cryptoOptions.srtpEnableAes128Sha1_32CryptoCipher, + newConfig.cryptoOptions.srtpEnableAes128Sha1_32CryptoCipher); + EXPECT_EQ(config.cryptoOptions.srtpEnableEncryptedRtpHeaderExtensions, + newConfig.cryptoOptions.srtpEnableEncryptedRtpHeaderExtensions); + EXPECT_EQ(config.cryptoOptions.sframeRequireFrameEncryption, + newConfig.cryptoOptions.sframeRequireFrameEncryption); } @end