Revert "Remove code supporting the SDES crypto mode in SDP"
This reverts commit ee212a72f220641f0a4a23fb2c1bd600a9069440. Reason for revert: Don't remove until downstream issues resolved Original change's description: > Remove code supporting the SDES crypto mode in SDP > > Removes the ability to accept nonencrypted answers to encrypted offers. > Fixes some logic around bundled sessions and requirement for > transport parameters. > > Bug: webrtc:11066 > Change-Id: I56d8628d223614918a1e5260fdb8a117c8c02dbd > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/236344 > Commit-Queue: Harald Alvestrand <hta@webrtc.org> > Reviewed-by: Niels Moller <nisse@webrtc.org> > Cr-Commit-Position: refs/heads/main@{#35298} # Not skipping CQ checks because original CL landed > 1 day ago. Bug: webrtc:11066 Change-Id: I0c400ceffe1b08e0be7b44abbb54c8a032128f05 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/237223 Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Henrik Boström <hbos@webrtc.org> Commit-Queue: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#35312}
This commit is contained in:
parent
c13e786c8f
commit
0d018415d5
@ -133,6 +133,7 @@ rtc_library("libjingle_peerconnection_api") {
|
||||
sources = [
|
||||
"candidate.cc",
|
||||
"candidate.h",
|
||||
"crypto_params.h",
|
||||
"data_channel_interface.cc",
|
||||
"data_channel_interface.h",
|
||||
"dtls_transport_interface.cc",
|
||||
|
||||
41
api/crypto_params.h
Normal file
41
api/crypto_params.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2004 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 API_CRYPTO_PARAMS_H_
|
||||
#define API_CRYPTO_PARAMS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// Parameters for SRTP negotiation, as described in RFC 4568.
|
||||
// TODO(benwright) - Rename to SrtpCryptoParams as these only apply to SRTP and
|
||||
// not generic crypto parameters for WebRTC.
|
||||
struct CryptoParams {
|
||||
CryptoParams() : tag(0) {}
|
||||
CryptoParams(int t,
|
||||
const std::string& cs,
|
||||
const std::string& kp,
|
||||
const std::string& sp)
|
||||
: tag(t), cipher_suite(cs), key_params(kp), session_params(sp) {}
|
||||
|
||||
bool Matches(const CryptoParams& params) const {
|
||||
return (tag == params.tag && cipher_suite == params.cipher_suite);
|
||||
}
|
||||
|
||||
int tag;
|
||||
std::string cipher_suite;
|
||||
std::string key_params;
|
||||
std::string session_params;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // API_CRYPTO_PARAMS_H_
|
||||
@ -24,6 +24,17 @@
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// SEC_ENABLED and SEC_REQUIRED should only be used if the session
|
||||
// was negotiated over TLS, to protect the inline crypto material
|
||||
// exchange.
|
||||
// SEC_DISABLED: No crypto in outgoing offer, ignore any supplied crypto.
|
||||
// SEC_ENABLED: Crypto in outgoing offer and answer (if supplied in offer).
|
||||
// SEC_REQUIRED: Crypto in outgoing offer and answer. Fail any offer with absent
|
||||
// or unsupported crypto.
|
||||
// TODO(deadbeef): Remove this or rename it to something more appropriate, like
|
||||
// SdesPolicy.
|
||||
enum SecurePolicy { SEC_DISABLED, SEC_ENABLED, SEC_REQUIRED };
|
||||
|
||||
// Whether our side of the call is driving the negotiation, or the other side.
|
||||
enum IceRole { ICEROLE_CONTROLLING = 0, ICEROLE_CONTROLLED, ICEROLE_UNKNOWN };
|
||||
|
||||
|
||||
@ -21,7 +21,8 @@
|
||||
|
||||
namespace cricket {
|
||||
|
||||
TransportDescriptionFactory::TransportDescriptionFactory() {}
|
||||
TransportDescriptionFactory::TransportDescriptionFactory()
|
||||
: secure_(SEC_DISABLED) {}
|
||||
|
||||
TransportDescriptionFactory::~TransportDescriptionFactory() = default;
|
||||
|
||||
@ -46,7 +47,7 @@ std::unique_ptr<TransportDescription> TransportDescriptionFactory::CreateOffer(
|
||||
}
|
||||
|
||||
// If we are trying to establish a secure transport, add a fingerprint.
|
||||
if (IsEncrypted()) {
|
||||
if (secure_ == SEC_ENABLED || secure_ == SEC_REQUIRED) {
|
||||
// Fail if we can't create the fingerprint.
|
||||
// If we are the initiator set role to "actpass".
|
||||
if (!SetSecurityInfo(desc.get(), CONNECTIONROLE_ACTPASS)) {
|
||||
@ -89,7 +90,7 @@ std::unique_ptr<TransportDescription> TransportDescriptionFactory::CreateAnswer(
|
||||
// Negotiate security params.
|
||||
if (offer && offer->identity_fingerprint.get()) {
|
||||
// The offer supports DTLS, so answer with DTLS, as long as we support it.
|
||||
if (IsEncrypted()) {
|
||||
if (secure_ == SEC_ENABLED || secure_ == SEC_REQUIRED) {
|
||||
ConnectionRole role = CONNECTIONROLE_NONE;
|
||||
// If the offer does not constrain the role, go with preference.
|
||||
if (offer->connection_role == CONNECTIONROLE_ACTPASS) {
|
||||
@ -115,7 +116,7 @@ std::unique_ptr<TransportDescription> TransportDescriptionFactory::CreateAnswer(
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
} else if (require_transport_attributes && IsEncrypted()) {
|
||||
} else if (require_transport_attributes && secure_ == SEC_REQUIRED) {
|
||||
// We require DTLS, but the other side didn't offer it. Fail.
|
||||
RTC_LOG(LS_WARNING) << "Failed to create TransportDescription answer "
|
||||
"because of incompatible security settings";
|
||||
|
||||
@ -40,20 +40,20 @@ class TransportDescriptionFactory {
|
||||
TransportDescriptionFactory();
|
||||
~TransportDescriptionFactory();
|
||||
|
||||
SecurePolicy secure() const { return secure_; }
|
||||
// The certificate to use when setting up DTLS.
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate() const {
|
||||
return certificate_;
|
||||
}
|
||||
|
||||
// Specifies the certificate to use.
|
||||
// When a certificate is set, transport will be encrypted.
|
||||
// Specifies the transport security policy to use.
|
||||
void set_secure(SecurePolicy s) { secure_ = s; }
|
||||
// Specifies the certificate to use (only used when secure != SEC_DISABLED).
|
||||
void set_certificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
|
||||
certificate_ = certificate;
|
||||
}
|
||||
|
||||
bool IsEncrypted() const { return certificate_ != nullptr; }
|
||||
|
||||
// Creates a transport description suitable for use in an offer.
|
||||
std::unique_ptr<TransportDescription> CreateOffer(
|
||||
const TransportOptions& options,
|
||||
@ -77,6 +77,7 @@ class TransportDescriptionFactory {
|
||||
bool SetSecurityInfo(TransportDescription* description,
|
||||
ConnectionRole role) const;
|
||||
|
||||
SecurePolicy secure_;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
|
||||
};
|
||||
|
||||
|
||||
@ -144,19 +144,17 @@ class TransportDescriptionFactoryTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetDtls(bool f1_dtls, bool f2_dtls) {
|
||||
if (f1_dtls) {
|
||||
void SetDtls(bool dtls) {
|
||||
if (dtls) {
|
||||
f1_.set_secure(cricket::SEC_ENABLED);
|
||||
f2_.set_secure(cricket::SEC_ENABLED);
|
||||
f1_.set_certificate(cert1_);
|
||||
} else {
|
||||
f1_.set_certificate(nullptr);
|
||||
}
|
||||
if (f2_dtls) {
|
||||
f2_.set_certificate(cert2_);
|
||||
} else {
|
||||
f2_.set_certificate(nullptr);
|
||||
f1_.set_secure(cricket::SEC_DISABLED);
|
||||
f2_.set_secure(cricket::SEC_DISABLED);
|
||||
}
|
||||
}
|
||||
void SetDtls(bool dtls) { SetDtls(dtls, dtls); }
|
||||
|
||||
cricket::IceCredentialsIterator ice_credentials_;
|
||||
TransportDescriptionFactory f1_;
|
||||
@ -173,19 +171,33 @@ TEST_F(TransportDescriptionFactoryTest, TestOfferDefault) {
|
||||
}
|
||||
|
||||
TEST_F(TransportDescriptionFactoryTest, TestOfferDtls) {
|
||||
SetDtls(true);
|
||||
f1_.set_secure(cricket::SEC_ENABLED);
|
||||
f1_.set_certificate(cert1_);
|
||||
std::string digest_alg;
|
||||
ASSERT_TRUE(
|
||||
cert1_->GetSSLCertificate().GetSignatureDigestAlgorithm(&digest_alg));
|
||||
std::unique_ptr<TransportDescription> desc =
|
||||
f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
|
||||
CheckDesc(desc.get(), "", "", "", digest_alg);
|
||||
// Ensure it also works with SEC_REQUIRED.
|
||||
f1_.set_secure(cricket::SEC_REQUIRED);
|
||||
desc = f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
|
||||
CheckDesc(desc.get(), "", "", "", digest_alg);
|
||||
}
|
||||
|
||||
// Test generating an offer with DTLS fails with no identity.
|
||||
TEST_F(TransportDescriptionFactoryTest, TestOfferDtlsWithNoIdentity) {
|
||||
f1_.set_secure(cricket::SEC_ENABLED);
|
||||
std::unique_ptr<TransportDescription> desc =
|
||||
f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
|
||||
ASSERT_TRUE(desc.get() == NULL);
|
||||
}
|
||||
|
||||
// Test updating an offer with DTLS to pick ICE.
|
||||
// The ICE credentials should stay the same in the new offer.
|
||||
TEST_F(TransportDescriptionFactoryTest, TestOfferDtlsReofferDtls) {
|
||||
SetDtls(true);
|
||||
f1_.set_secure(cricket::SEC_ENABLED);
|
||||
f1_.set_certificate(cert1_);
|
||||
std::string digest_alg;
|
||||
ASSERT_TRUE(
|
||||
cert1_->GetSSLCertificate().GetSignatureDigestAlgorithm(&digest_alg));
|
||||
@ -225,7 +237,8 @@ TEST_F(TransportDescriptionFactoryTest, TestReanswer) {
|
||||
|
||||
// Test that we handle answering an offer with DTLS with no DTLS.
|
||||
TEST_F(TransportDescriptionFactoryTest, TestAnswerDtlsToNoDtls) {
|
||||
SetDtls(true, false);
|
||||
f1_.set_secure(cricket::SEC_ENABLED);
|
||||
f1_.set_certificate(cert1_);
|
||||
std::unique_ptr<TransportDescription> offer =
|
||||
f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
@ -234,20 +247,31 @@ TEST_F(TransportDescriptionFactoryTest, TestAnswerDtlsToNoDtls) {
|
||||
CheckDesc(desc.get(), "", "", "", "");
|
||||
}
|
||||
|
||||
// Test that we reject answering an offer without DTLS if we have DTLS enabled.
|
||||
// Test that we handle answering an offer without DTLS if we have DTLS enabled,
|
||||
// but fail if we require DTLS.
|
||||
TEST_F(TransportDescriptionFactoryTest, TestAnswerNoDtlsToDtls) {
|
||||
SetDtls(false, true);
|
||||
f2_.set_secure(cricket::SEC_ENABLED);
|
||||
f2_.set_certificate(cert2_);
|
||||
std::unique_ptr<TransportDescription> offer =
|
||||
f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
std::unique_ptr<TransportDescription> desc = f2_.CreateAnswer(
|
||||
offer.get(), TransportOptions(), true, NULL, &ice_credentials_);
|
||||
ASSERT_FALSE(desc);
|
||||
CheckDesc(desc.get(), "", "", "", "");
|
||||
f2_.set_secure(cricket::SEC_REQUIRED);
|
||||
desc = f2_.CreateAnswer(offer.get(), TransportOptions(), true, NULL,
|
||||
&ice_credentials_);
|
||||
ASSERT_TRUE(desc.get() == NULL);
|
||||
}
|
||||
|
||||
// Test that we handle answering an DTLS offer with DTLS.
|
||||
// Test that we handle answering an DTLS offer with DTLS, both if we have
|
||||
// DTLS enabled and required.
|
||||
TEST_F(TransportDescriptionFactoryTest, TestAnswerDtlsToDtls) {
|
||||
SetDtls(true, true);
|
||||
f1_.set_secure(cricket::SEC_ENABLED);
|
||||
f1_.set_certificate(cert1_);
|
||||
|
||||
f2_.set_secure(cricket::SEC_ENABLED);
|
||||
f2_.set_certificate(cert2_);
|
||||
// f2_ produces the answer that is being checked in this test, so the
|
||||
// answer must contain fingerprint lines with cert2_'s digest algorithm.
|
||||
std::string digest_alg2;
|
||||
@ -260,6 +284,10 @@ TEST_F(TransportDescriptionFactoryTest, TestAnswerDtlsToDtls) {
|
||||
std::unique_ptr<TransportDescription> desc = f2_.CreateAnswer(
|
||||
offer.get(), TransportOptions(), true, NULL, &ice_credentials_);
|
||||
CheckDesc(desc.get(), "", "", "", digest_alg2);
|
||||
f2_.set_secure(cricket::SEC_REQUIRED);
|
||||
desc = f2_.CreateAnswer(offer.get(), TransportOptions(), true, NULL,
|
||||
&ice_credentials_);
|
||||
CheckDesc(desc.get(), "", "", "", digest_alg2);
|
||||
}
|
||||
|
||||
// Test that ice ufrag and password is changed in an updated offer and answer
|
||||
@ -326,7 +354,11 @@ TEST_F(TransportDescriptionFactoryTest, CreateAnswerIceCredentialsIterator) {
|
||||
}
|
||||
|
||||
TEST_F(TransportDescriptionFactoryTest, CreateAnswerToDtlsActpassOffer) {
|
||||
SetDtls(true);
|
||||
f1_.set_secure(cricket::SEC_ENABLED);
|
||||
f1_.set_certificate(cert1_);
|
||||
|
||||
f2_.set_secure(cricket::SEC_ENABLED);
|
||||
f2_.set_certificate(cert2_);
|
||||
cricket::TransportOptions options;
|
||||
std::unique_ptr<TransportDescription> offer =
|
||||
f1_.CreateOffer(options, nullptr, &ice_credentials_);
|
||||
@ -337,7 +369,11 @@ TEST_F(TransportDescriptionFactoryTest, CreateAnswerToDtlsActpassOffer) {
|
||||
}
|
||||
|
||||
TEST_F(TransportDescriptionFactoryTest, CreateAnswerToDtlsActiveOffer) {
|
||||
SetDtls(true);
|
||||
f1_.set_secure(cricket::SEC_ENABLED);
|
||||
f1_.set_certificate(cert1_);
|
||||
|
||||
f2_.set_secure(cricket::SEC_ENABLED);
|
||||
f2_.set_certificate(cert2_);
|
||||
cricket::TransportOptions options;
|
||||
std::unique_ptr<TransportDescription> offer =
|
||||
f1_.CreateOffer(options, nullptr, &ice_credentials_);
|
||||
@ -349,7 +385,11 @@ TEST_F(TransportDescriptionFactoryTest, CreateAnswerToDtlsActiveOffer) {
|
||||
}
|
||||
|
||||
TEST_F(TransportDescriptionFactoryTest, CreateAnswerToDtlsPassiveOffer) {
|
||||
SetDtls(true);
|
||||
f1_.set_secure(cricket::SEC_ENABLED);
|
||||
f1_.set_certificate(cert1_);
|
||||
|
||||
f2_.set_secure(cricket::SEC_ENABLED);
|
||||
f2_.set_certificate(cert2_);
|
||||
cricket::TransportOptions options;
|
||||
std::unique_ptr<TransportDescription> offer =
|
||||
f1_.CreateOffer(options, nullptr, &ice_credentials_);
|
||||
|
||||
@ -81,6 +81,8 @@ rtc_library("rtc_pc_base") {
|
||||
"sctp_transport.h",
|
||||
"sctp_utils.cc",
|
||||
"sctp_utils.h",
|
||||
"srtp_filter.cc",
|
||||
"srtp_filter.h",
|
||||
"srtp_session.cc",
|
||||
"srtp_session.h",
|
||||
"srtp_transport.cc",
|
||||
@ -884,6 +886,7 @@ if (rtc_include_tests && !build_with_chromium) {
|
||||
"rtp_transport_unittest.cc",
|
||||
"sctp_transport_unittest.cc",
|
||||
"session_description_unittest.cc",
|
||||
"srtp_filter_unittest.cc",
|
||||
"srtp_session_unittest.cc",
|
||||
"srtp_transport_unittest.cc",
|
||||
"test/rtp_transport_test_util.h",
|
||||
|
||||
@ -47,6 +47,7 @@
|
||||
#include "pc/rtp_transport.h"
|
||||
#include "pc/rtp_transport_internal.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/srtp_filter.h"
|
||||
#include "pc/srtp_transport.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/async_udp_socket.h"
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/crypto_params.h"
|
||||
#include "api/dtls_transport_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "p2p/base/dtls_transport_internal.h"
|
||||
@ -48,6 +49,15 @@ class DtlsSrtpTransport : public SrtpTransport {
|
||||
|
||||
void SetOnDtlsStateChange(std::function<void(void)> callback);
|
||||
|
||||
RTCError SetSrtpSendKey(const cricket::CryptoParams& params) override {
|
||||
return RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
|
||||
"Set SRTP keys for DTLS-SRTP is not supported.");
|
||||
}
|
||||
RTCError SetSrtpReceiveKey(const cricket::CryptoParams& params) override {
|
||||
return RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
|
||||
"Set SRTP keys for DTLS-SRTP is not supported.");
|
||||
}
|
||||
|
||||
// If `active_reset_srtp_params_` is set to be true, the SRTP parameters will
|
||||
// be reset whenever the DtlsTransports are reset.
|
||||
void SetActiveResetSrtpParams(bool active_reset_srtp_params) {
|
||||
|
||||
@ -37,10 +37,12 @@ JsepTransportDescription::JsepTransportDescription() {}
|
||||
|
||||
JsepTransportDescription::JsepTransportDescription(
|
||||
bool rtcp_mux_enabled,
|
||||
const std::vector<CryptoParams>& cryptos,
|
||||
const std::vector<int>& encrypted_header_extension_ids,
|
||||
int rtp_abs_sendtime_extn_id,
|
||||
const TransportDescription& transport_desc)
|
||||
: rtcp_mux_enabled(rtcp_mux_enabled),
|
||||
cryptos(cryptos),
|
||||
encrypted_header_extension_ids(encrypted_header_extension_ids),
|
||||
rtp_abs_sendtime_extn_id(rtp_abs_sendtime_extn_id),
|
||||
transport_desc(transport_desc) {}
|
||||
@ -48,6 +50,7 @@ JsepTransportDescription::JsepTransportDescription(
|
||||
JsepTransportDescription::JsepTransportDescription(
|
||||
const JsepTransportDescription& from)
|
||||
: rtcp_mux_enabled(from.rtcp_mux_enabled),
|
||||
cryptos(from.cryptos),
|
||||
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) {}
|
||||
@ -60,6 +63,7 @@ JsepTransportDescription& JsepTransportDescription::operator=(
|
||||
return *this;
|
||||
}
|
||||
rtcp_mux_enabled = from.rtcp_mux_enabled;
|
||||
cryptos = from.cryptos;
|
||||
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;
|
||||
@ -85,6 +89,7 @@ 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)),
|
||||
dtls_srtp_transport_(std::move(dtls_srtp_transport)),
|
||||
rtp_dtls_transport_(rtp_dtls_transport
|
||||
? rtc::make_ref_counted<webrtc::DtlsTransport>(
|
||||
@ -112,10 +117,15 @@ 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);
|
||||
RTC_DCHECK(!dtls_srtp_transport);
|
||||
} else if (sdes_transport_) {
|
||||
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);
|
||||
}
|
||||
|
||||
if (sctp_transport_) {
|
||||
@ -162,8 +172,19 @@ webrtc::RTCError JsepTransport::SetLocalJsepTransportDescription(
|
||||
"Failed to setup RTCP mux.");
|
||||
}
|
||||
|
||||
if (dtls_srtp_transport_) {
|
||||
// If doing SDES, setup the SDES crypto parameters.
|
||||
if (sdes_transport_) {
|
||||
RTC_DCHECK(!unencrypted_rtp_transport_);
|
||||
RTC_DCHECK(!dtls_srtp_transport_);
|
||||
if (!SetSdes(jsep_description.cryptos,
|
||||
jsep_description.encrypted_header_extension_ids, type,
|
||||
ContentSource::CS_LOCAL)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to setup SDES crypto parameters.");
|
||||
}
|
||||
} else if (dtls_srtp_transport_) {
|
||||
RTC_DCHECK(!unencrypted_rtp_transport_);
|
||||
RTC_DCHECK(!sdes_transport_);
|
||||
dtls_srtp_transport_->UpdateRecvEncryptedHeaderExtensionIds(
|
||||
jsep_description.encrypted_header_extension_ids);
|
||||
}
|
||||
@ -240,8 +261,21 @@ webrtc::RTCError JsepTransport::SetRemoteJsepTransportDescription(
|
||||
"Failed to setup RTCP mux.");
|
||||
}
|
||||
|
||||
if (dtls_srtp_transport_) {
|
||||
// If doing SDES, setup the SDES crypto parameters.
|
||||
if (sdes_transport_) {
|
||||
RTC_DCHECK(!unencrypted_rtp_transport_);
|
||||
RTC_DCHECK(!dtls_srtp_transport_);
|
||||
if (!SetSdes(jsep_description.cryptos,
|
||||
jsep_description.encrypted_header_extension_ids, type,
|
||||
ContentSource::CS_REMOTE)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to setup SDES crypto parameters.");
|
||||
}
|
||||
sdes_transport_->CacheRtpAbsSendTimeHeaderExtension(
|
||||
jsep_description.rtp_abs_sendtime_extn_id);
|
||||
} else if (dtls_srtp_transport_) {
|
||||
RTC_DCHECK(!unencrypted_rtp_transport_);
|
||||
RTC_DCHECK(!sdes_transport_);
|
||||
dtls_srtp_transport_->UpdateSendEncryptedHeaderExtensionIds(
|
||||
jsep_description.encrypted_header_extension_ids);
|
||||
dtls_srtp_transport_->CacheRtpAbsSendTimeHeaderExtension(
|
||||
@ -440,11 +474,17 @@ bool JsepTransport::SetRtcpMux(bool enable,
|
||||
|
||||
void JsepTransport::ActivateRtcpMux() {
|
||||
if (unencrypted_rtp_transport_) {
|
||||
RTC_DCHECK(!sdes_transport_);
|
||||
RTC_DCHECK(!dtls_srtp_transport_);
|
||||
unencrypted_rtp_transport_->SetRtcpPacketTransport(nullptr);
|
||||
} else if (sdes_transport_) {
|
||||
RTC_DCHECK(!unencrypted_rtp_transport_);
|
||||
RTC_DCHECK(!dtls_srtp_transport_);
|
||||
sdes_transport_->SetRtcpPacketTransport(nullptr);
|
||||
} else if (dtls_srtp_transport_) {
|
||||
RTC_DCHECK(dtls_srtp_transport_);
|
||||
RTC_DCHECK(!unencrypted_rtp_transport_);
|
||||
RTC_DCHECK(!sdes_transport_);
|
||||
dtls_srtp_transport_->SetDtlsTransports(rtp_dtls_transport(),
|
||||
/*rtcp_dtls_transport=*/nullptr);
|
||||
}
|
||||
@ -453,6 +493,51 @@ void JsepTransport::ActivateRtcpMux() {
|
||||
rtcp_mux_active_callback_();
|
||||
}
|
||||
|
||||
bool JsepTransport::SetSdes(const std::vector<CryptoParams>& cryptos,
|
||||
const std::vector<int>& encrypted_extension_ids,
|
||||
webrtc::SdpType type,
|
||||
ContentSource source) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
bool ret = false;
|
||||
ret = sdes_negotiator_.Process(cryptos, type, source);
|
||||
if (!ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (source == ContentSource::CS_LOCAL) {
|
||||
recv_extension_ids_ = encrypted_extension_ids;
|
||||
} else {
|
||||
send_extension_ids_ = encrypted_extension_ids;
|
||||
}
|
||||
|
||||
// If setting an SDES answer succeeded, apply the negotiated parameters
|
||||
// to the SRTP transport.
|
||||
if ((type == SdpType::kPrAnswer || type == SdpType::kAnswer) && ret) {
|
||||
if (sdes_negotiator_.send_cipher_suite() &&
|
||||
sdes_negotiator_.recv_cipher_suite()) {
|
||||
RTC_DCHECK(send_extension_ids_);
|
||||
RTC_DCHECK(recv_extension_ids_);
|
||||
ret = sdes_transport_->SetRtpParams(
|
||||
*(sdes_negotiator_.send_cipher_suite()),
|
||||
sdes_negotiator_.send_key().data(),
|
||||
static_cast<int>(sdes_negotiator_.send_key().size()),
|
||||
*(send_extension_ids_), *(sdes_negotiator_.recv_cipher_suite()),
|
||||
sdes_negotiator_.recv_key().data(),
|
||||
static_cast<int>(sdes_negotiator_.recv_key().size()),
|
||||
*(recv_extension_ids_));
|
||||
} else {
|
||||
RTC_LOG(LS_INFO) << "No crypto keys are provided for SDES.";
|
||||
if (type == SdpType::kAnswer) {
|
||||
// Explicitly reset the `sdes_transport_` if no crypto param is
|
||||
// provided in the answer. No need to call `ResetParams()` for
|
||||
// `sdes_negotiator_` because it resets the params inside `SetAnswer`.
|
||||
sdes_transport_->ResetParams();
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
webrtc::RTCError JsepTransport::NegotiateAndSetDtlsParameters(
|
||||
SdpType local_description_type) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/crypto_params.h"
|
||||
#include "api/ice_transport_interface.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/rtc_error.h"
|
||||
@ -39,6 +40,8 @@
|
||||
#include "pc/rtp_transport_internal.h"
|
||||
#include "pc/sctp_transport.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "pc/srtp_filter.h"
|
||||
#include "pc/srtp_transport.h"
|
||||
#include "pc/transport_stats.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
@ -57,6 +60,7 @@ struct JsepTransportDescription {
|
||||
JsepTransportDescription();
|
||||
JsepTransportDescription(
|
||||
bool rtcp_mux_enabled,
|
||||
const std::vector<CryptoParams>& cryptos,
|
||||
const std::vector<int>& encrypted_header_extension_ids,
|
||||
int rtp_abs_sendtime_extn_id,
|
||||
const TransportDescription& transport_description);
|
||||
@ -66,6 +70,7 @@ struct JsepTransportDescription {
|
||||
JsepTransportDescription& operator=(const JsepTransportDescription& from);
|
||||
|
||||
bool rtcp_mux_enabled = true;
|
||||
std::vector<CryptoParams> cryptos;
|
||||
std::vector<int> encrypted_header_extension_ids;
|
||||
int rtp_abs_sendtime_extn_id = -1;
|
||||
// TODO(zhihuang): Add the ICE and DTLS related variables and methods from
|
||||
@ -166,6 +171,9 @@ class JsepTransport {
|
||||
if (dtls_srtp_transport_) {
|
||||
return dtls_srtp_transport_.get();
|
||||
}
|
||||
if (sdes_transport_) {
|
||||
return sdes_transport_.get();
|
||||
}
|
||||
if (unencrypted_rtp_transport_) {
|
||||
return unencrypted_rtp_transport_.get();
|
||||
}
|
||||
@ -236,6 +244,11 @@ class JsepTransport {
|
||||
|
||||
void ActivateRtcpMux() RTC_RUN_ON(network_thread_);
|
||||
|
||||
bool SetSdes(const std::vector<CryptoParams>& cryptos,
|
||||
const std::vector<int>& 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.
|
||||
@ -287,6 +300,7 @@ 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<webrtc::RtpTransport> unencrypted_rtp_transport_;
|
||||
const std::unique_ptr<webrtc::SrtpTransport> sdes_transport_;
|
||||
const std::unique_ptr<webrtc::DtlsSrtpTransport> dtls_srtp_transport_;
|
||||
|
||||
const rtc::scoped_refptr<webrtc::DtlsTransport> rtp_dtls_transport_;
|
||||
@ -299,6 +313,7 @@ class JsepTransport {
|
||||
sctp_data_channel_transport_;
|
||||
const rtc::scoped_refptr<webrtc::SctpTransport> sctp_transport_;
|
||||
|
||||
SrtpFilter sdes_negotiator_ RTC_GUARDED_BY(network_thread_);
|
||||
RtcpMuxFilter rtcp_mux_negotiator_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
// Cache the encrypted header extension IDs for SDES negoitation.
|
||||
|
||||
@ -918,8 +918,8 @@ JsepTransportController::CreateJsepTransportDescription(
|
||||
: content_desc->rtcp_mux();
|
||||
|
||||
return cricket::JsepTransportDescription(
|
||||
rtcp_mux_enabled, encrypted_extension_ids, rtp_abs_sendtime_extn_id,
|
||||
transport_info.description);
|
||||
rtcp_mux_enabled, content_desc->cryptos(), encrypted_extension_ids,
|
||||
rtp_abs_sendtime_extn_id, transport_info.description);
|
||||
}
|
||||
|
||||
std::vector<int> JsepTransportController::GetEncryptedHeaderExtensionIds(
|
||||
@ -1017,6 +1017,12 @@ RTCError JsepTransportController::MaybeCreateJsepTransport(
|
||||
if (transport) {
|
||||
return RTCError::OK();
|
||||
}
|
||||
const cricket::MediaContentDescription* content_desc =
|
||||
content_info.media_description();
|
||||
if (certificate_ && !content_desc->cryptos().empty()) {
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
"SDES and DTLS-SRTP cannot be enabled at the same time.");
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<webrtc::IceTransportInterface> ice =
|
||||
CreateIceTransport(content_info.name, /*rtcp=*/false);
|
||||
@ -1044,6 +1050,10 @@ 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->cryptos().empty()) {
|
||||
sdes_transport = CreateSdesTransport(
|
||||
content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
|
||||
RTC_LOG(LS_INFO) << "Creating SdesTransport.";
|
||||
} else {
|
||||
RTC_LOG(LS_INFO) << "Creating DtlsSrtpTransport.";
|
||||
dtls_srtp_transport = CreateDtlsSrtpTransport(
|
||||
|
||||
@ -30,6 +30,11 @@ static const char kIceUfrag2[] = "U002";
|
||||
static const char kIcePwd2[] = "TESTIEPWD00000000000002";
|
||||
static const char kTransportName[] = "Test Transport";
|
||||
|
||||
enum class SrtpMode {
|
||||
kSdes,
|
||||
kDtlsSrtp,
|
||||
};
|
||||
|
||||
struct NegotiateRoleParams {
|
||||
ConnectionRole local_role;
|
||||
ConnectionRole remote_role;
|
||||
@ -87,7 +92,8 @@ class JsepTransport2Test : public ::testing::Test, public sigslot::has_slots<> {
|
||||
|
||||
// Create a new JsepTransport with a FakeDtlsTransport and a
|
||||
// FakeIceTransport.
|
||||
std::unique_ptr<JsepTransport> CreateJsepTransport2(bool rtcp_mux_enabled) {
|
||||
std::unique_ptr<JsepTransport> CreateJsepTransport2(bool rtcp_mux_enabled,
|
||||
SrtpMode srtp_mode) {
|
||||
auto ice_internal = std::make_unique<FakeIceTransport>(
|
||||
kTransportName, ICE_CANDIDATE_COMPONENT_RTP);
|
||||
auto rtp_dtls_transport =
|
||||
@ -107,8 +113,19 @@ class JsepTransport2Test : public ::testing::Test, public sigslot::has_slots<> {
|
||||
std::unique_ptr<webrtc::RtpTransport> unencrypted_rtp_transport;
|
||||
std::unique_ptr<webrtc::SrtpTransport> sdes_transport;
|
||||
std::unique_ptr<webrtc::DtlsSrtpTransport> dtls_srtp_transport;
|
||||
dtls_srtp_transport = CreateDtlsSrtpTransport(rtp_dtls_transport.get(),
|
||||
rtcp_dtls_transport.get());
|
||||
switch (srtp_mode) {
|
||||
case SrtpMode::kSdes:
|
||||
sdes_transport = CreateSdesTransport(rtp_dtls_transport.get(),
|
||||
rtcp_dtls_transport.get());
|
||||
sdes_transport_ = sdes_transport.get();
|
||||
break;
|
||||
case SrtpMode::kDtlsSrtp:
|
||||
dtls_srtp_transport = CreateDtlsSrtpTransport(
|
||||
rtp_dtls_transport.get(), rtcp_dtls_transport.get());
|
||||
break;
|
||||
default:
|
||||
RTC_NOTREACHED();
|
||||
}
|
||||
|
||||
auto jsep_transport = std::make_unique<JsepTransport>(
|
||||
kTransportName, /*local_certificate=*/nullptr, std::move(ice),
|
||||
@ -167,7 +184,7 @@ class JsepTransport2WithRtcpMux : public JsepTransport2Test,
|
||||
// This test verifies the ICE parameters are properly applied to the transports.
|
||||
TEST_P(JsepTransport2WithRtcpMux, SetIceParameters) {
|
||||
bool rtcp_mux_enabled = GetParam();
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
|
||||
JsepTransportDescription jsep_description;
|
||||
jsep_description.transport_desc = TransportDescription(kIceUfrag1, kIcePwd1);
|
||||
@ -213,7 +230,7 @@ TEST_P(JsepTransport2WithRtcpMux, SetIceParameters) {
|
||||
// Similarly, test DTLS parameters are properly applied to the transports.
|
||||
TEST_P(JsepTransport2WithRtcpMux, SetDtlsParameters) {
|
||||
bool rtcp_mux_enabled = GetParam();
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
|
||||
// Create certificates.
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> local_cert =
|
||||
@ -264,7 +281,7 @@ TEST_P(JsepTransport2WithRtcpMux, SetDtlsParameters) {
|
||||
// CONNECTIONROLE_PASSIVE, expecting SSL_CLIENT role.
|
||||
TEST_P(JsepTransport2WithRtcpMux, SetDtlsParametersWithPassiveAnswer) {
|
||||
bool rtcp_mux_enabled = GetParam();
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
|
||||
// Create certificates.
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> local_cert =
|
||||
@ -316,7 +333,7 @@ TEST_P(JsepTransport2WithRtcpMux, SetDtlsParametersWithPassiveAnswer) {
|
||||
// only starts returning "false" once an ICE restart has been initiated.
|
||||
TEST_P(JsepTransport2WithRtcpMux, NeedsIceRestart) {
|
||||
bool rtcp_mux_enabled = GetParam();
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
|
||||
// Use the same JsepTransportDescription for both offer and answer.
|
||||
JsepTransportDescription description;
|
||||
@ -361,7 +378,7 @@ TEST_P(JsepTransport2WithRtcpMux, NeedsIceRestart) {
|
||||
|
||||
TEST_P(JsepTransport2WithRtcpMux, GetStats) {
|
||||
bool rtcp_mux_enabled = GetParam();
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
|
||||
size_t expected_stats_size = rtcp_mux_enabled ? 1u : 2u;
|
||||
TransportStats stats;
|
||||
@ -377,7 +394,7 @@ TEST_P(JsepTransport2WithRtcpMux, GetStats) {
|
||||
// certificate matches the fingerprint.
|
||||
TEST_P(JsepTransport2WithRtcpMux, VerifyCertificateFingerprint) {
|
||||
bool rtcp_mux_enabled = GetParam();
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
|
||||
EXPECT_FALSE(
|
||||
jsep_transport_->VerifyCertificateFingerprint(nullptr, nullptr).ok());
|
||||
@ -451,7 +468,8 @@ TEST_P(JsepTransport2WithRtcpMux, ValidDtlsRoleNegotiation) {
|
||||
};
|
||||
|
||||
for (auto& param : valid_client_params) {
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ =
|
||||
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
jsep_transport_->SetLocalCertificate(certificate);
|
||||
|
||||
local_description.transport_desc.connection_role = param.local_role;
|
||||
@ -496,7 +514,8 @@ TEST_P(JsepTransport2WithRtcpMux, ValidDtlsRoleNegotiation) {
|
||||
};
|
||||
|
||||
for (auto& param : valid_server_params) {
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ =
|
||||
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
jsep_transport_->SetLocalCertificate(certificate);
|
||||
|
||||
local_description.transport_desc.connection_role = param.local_role;
|
||||
@ -567,7 +586,8 @@ TEST_P(JsepTransport2WithRtcpMux, InvalidDtlsRoleNegotiation) {
|
||||
SdpType::kPrAnswer}};
|
||||
|
||||
for (auto& param : duplicate_params) {
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ =
|
||||
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
jsep_transport_->SetLocalCertificate(certificate);
|
||||
|
||||
local_description.transport_desc.connection_role = param.local_role;
|
||||
@ -617,7 +637,8 @@ TEST_P(JsepTransport2WithRtcpMux, InvalidDtlsRoleNegotiation) {
|
||||
SdpType::kPrAnswer}};
|
||||
|
||||
for (auto& param : offerer_without_actpass_params) {
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ =
|
||||
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
jsep_transport_->SetLocalCertificate(certificate);
|
||||
|
||||
local_description.transport_desc.connection_role = param.local_role;
|
||||
@ -663,7 +684,7 @@ TEST_F(JsepTransport2Test, ValidDtlsReofferFromAnswerer) {
|
||||
rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("testing", rtc::KT_ECDSA));
|
||||
bool rtcp_mux_enabled = true;
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
jsep_transport_->SetLocalCertificate(certificate);
|
||||
|
||||
JsepTransportDescription local_offer =
|
||||
@ -710,7 +731,7 @@ TEST_F(JsepTransport2Test, InvalidDtlsReofferFromAnswerer) {
|
||||
rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("testing", rtc::KT_ECDSA));
|
||||
bool rtcp_mux_enabled = true;
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
jsep_transport_->SetLocalCertificate(certificate);
|
||||
|
||||
JsepTransportDescription local_offer =
|
||||
@ -756,7 +777,7 @@ TEST_F(JsepTransport2Test, RemoteOfferWithCurrentNegotiatedDtlsRole) {
|
||||
rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("testing", rtc::KT_ECDSA));
|
||||
bool rtcp_mux_enabled = true;
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
jsep_transport_->SetLocalCertificate(certificate);
|
||||
|
||||
JsepTransportDescription remote_desc =
|
||||
@ -801,7 +822,7 @@ TEST_F(JsepTransport2Test, RemoteOfferThatChangesNegotiatedDtlsRole) {
|
||||
rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("testing", rtc::KT_ECDSA));
|
||||
bool rtcp_mux_enabled = true;
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
jsep_transport_->SetLocalCertificate(certificate);
|
||||
|
||||
JsepTransportDescription remote_desc =
|
||||
@ -846,7 +867,7 @@ TEST_F(JsepTransport2Test, DtlsSetupWithLegacyAsAnswerer) {
|
||||
rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("testing", rtc::KT_ECDSA));
|
||||
bool rtcp_mux_enabled = true;
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled);
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
jsep_transport_->SetLocalCertificate(certificate);
|
||||
|
||||
JsepTransportDescription remote_desc =
|
||||
@ -878,7 +899,8 @@ TEST_F(JsepTransport2Test, DtlsSetupWithLegacyAsAnswerer) {
|
||||
// Tests that when the RTCP mux is successfully negotiated, the RTCP transport
|
||||
// will be destroyed and the SignalRtpMuxActive will be fired.
|
||||
TEST_F(JsepTransport2Test, RtcpMuxNegotiation) {
|
||||
jsep_transport_ = CreateJsepTransport2(/*rtcp_mux_enabled=*/false);
|
||||
jsep_transport_ =
|
||||
CreateJsepTransport2(/*rtcp_mux_enabled=*/false, SrtpMode::kDtlsSrtp);
|
||||
JsepTransportDescription local_desc;
|
||||
local_desc.rtcp_mux_enabled = true;
|
||||
ASSERT_NE(nullptr, jsep_transport_->rtcp_dtls_transport());
|
||||
@ -900,7 +922,8 @@ TEST_F(JsepTransport2Test, RtcpMuxNegotiation) {
|
||||
EXPECT_TRUE(signal_rtcp_mux_active_received_);
|
||||
|
||||
// The remote side doesn't support RTCP-mux.
|
||||
jsep_transport_ = CreateJsepTransport2(/*rtcp_mux_enabled=*/false);
|
||||
jsep_transport_ =
|
||||
CreateJsepTransport2(/*rtcp_mux_enabled=*/false, SrtpMode::kDtlsSrtp);
|
||||
signal_rtcp_mux_active_received_ = false;
|
||||
remote_desc.rtcp_mux_enabled = false;
|
||||
ASSERT_TRUE(
|
||||
@ -916,10 +939,87 @@ TEST_F(JsepTransport2Test, RtcpMuxNegotiation) {
|
||||
EXPECT_FALSE(signal_rtcp_mux_active_received_);
|
||||
}
|
||||
|
||||
TEST_F(JsepTransport2Test, SdesNegotiation) {
|
||||
jsep_transport_ =
|
||||
CreateJsepTransport2(/*rtcp_mux_enabled=*/true, SrtpMode::kSdes);
|
||||
ASSERT_TRUE(sdes_transport_);
|
||||
EXPECT_FALSE(sdes_transport_->IsSrtpActive());
|
||||
|
||||
JsepTransportDescription offer_desc;
|
||||
offer_desc.cryptos.push_back(cricket::CryptoParams(
|
||||
1, rtc::kCsAesCm128HmacSha1_32, "inline:" + rtc::CreateRandomString(40),
|
||||
std::string()));
|
||||
ASSERT_TRUE(
|
||||
jsep_transport_
|
||||
->SetLocalJsepTransportDescription(offer_desc, SdpType::kOffer)
|
||||
.ok());
|
||||
|
||||
JsepTransportDescription answer_desc;
|
||||
answer_desc.cryptos.push_back(cricket::CryptoParams(
|
||||
1, rtc::kCsAesCm128HmacSha1_32, "inline:" + rtc::CreateRandomString(40),
|
||||
std::string()));
|
||||
ASSERT_TRUE(
|
||||
jsep_transport_
|
||||
->SetRemoteJsepTransportDescription(answer_desc, SdpType::kAnswer)
|
||||
.ok());
|
||||
EXPECT_TRUE(sdes_transport_->IsSrtpActive());
|
||||
}
|
||||
|
||||
TEST_F(JsepTransport2Test, SdesNegotiationWithEmptyCryptosInAnswer) {
|
||||
jsep_transport_ =
|
||||
CreateJsepTransport2(/*rtcp_mux_enabled=*/true, SrtpMode::kSdes);
|
||||
ASSERT_TRUE(sdes_transport_);
|
||||
EXPECT_FALSE(sdes_transport_->IsSrtpActive());
|
||||
|
||||
JsepTransportDescription offer_desc;
|
||||
offer_desc.cryptos.push_back(cricket::CryptoParams(
|
||||
1, rtc::kCsAesCm128HmacSha1_32, "inline:" + rtc::CreateRandomString(40),
|
||||
std::string()));
|
||||
ASSERT_TRUE(
|
||||
jsep_transport_
|
||||
->SetLocalJsepTransportDescription(offer_desc, SdpType::kOffer)
|
||||
.ok());
|
||||
|
||||
JsepTransportDescription answer_desc;
|
||||
ASSERT_TRUE(
|
||||
jsep_transport_
|
||||
->SetRemoteJsepTransportDescription(answer_desc, SdpType::kAnswer)
|
||||
.ok());
|
||||
// SRTP is not active because the crypto parameter is answer is empty.
|
||||
EXPECT_FALSE(sdes_transport_->IsSrtpActive());
|
||||
}
|
||||
|
||||
TEST_F(JsepTransport2Test, SdesNegotiationWithMismatchedCryptos) {
|
||||
jsep_transport_ =
|
||||
CreateJsepTransport2(/*rtcp_mux_enabled=*/true, SrtpMode::kSdes);
|
||||
ASSERT_TRUE(sdes_transport_);
|
||||
EXPECT_FALSE(sdes_transport_->IsSrtpActive());
|
||||
|
||||
JsepTransportDescription offer_desc;
|
||||
offer_desc.cryptos.push_back(cricket::CryptoParams(
|
||||
1, rtc::kCsAesCm128HmacSha1_32, "inline:" + rtc::CreateRandomString(40),
|
||||
std::string()));
|
||||
ASSERT_TRUE(
|
||||
jsep_transport_
|
||||
->SetLocalJsepTransportDescription(offer_desc, SdpType::kOffer)
|
||||
.ok());
|
||||
|
||||
JsepTransportDescription answer_desc;
|
||||
answer_desc.cryptos.push_back(cricket::CryptoParams(
|
||||
1, rtc::kCsAesCm128HmacSha1_80, "inline:" + rtc::CreateRandomString(40),
|
||||
std::string()));
|
||||
// Expected to fail because the crypto parameters don't match.
|
||||
ASSERT_FALSE(
|
||||
jsep_transport_
|
||||
->SetRemoteJsepTransportDescription(answer_desc, SdpType::kAnswer)
|
||||
.ok());
|
||||
}
|
||||
|
||||
// Tests that the remote candidates can be added to the transports after both
|
||||
// local and remote descriptions are set.
|
||||
TEST_F(JsepTransport2Test, AddRemoteCandidates) {
|
||||
jsep_transport_ = CreateJsepTransport2(/*rtcp_mux_enabled=*/true);
|
||||
jsep_transport_ =
|
||||
CreateJsepTransport2(/*rtcp_mux_enabled=*/true, SrtpMode::kDtlsSrtp);
|
||||
auto fake_ice_transport = static_cast<FakeIceTransport*>(
|
||||
jsep_transport_->rtp_dtls_transport()->ice_transport());
|
||||
|
||||
@ -943,6 +1043,7 @@ TEST_F(JsepTransport2Test, AddRemoteCandidates) {
|
||||
}
|
||||
|
||||
enum class Scenario {
|
||||
kSdes,
|
||||
kDtlsBeforeCallerSendOffer,
|
||||
kDtlsBeforeCallerSetAnswer,
|
||||
kDtlsAfterCallerSetAnswer,
|
||||
@ -954,9 +1055,9 @@ class JsepTransport2HeaderExtensionTest
|
||||
protected:
|
||||
JsepTransport2HeaderExtensionTest() {}
|
||||
|
||||
void CreateJsepTransportPair() {
|
||||
jsep_transport1_ = CreateJsepTransport2(/*rtcp_mux_enabled=*/true);
|
||||
jsep_transport2_ = CreateJsepTransport2(/*rtcp_mux_enabled=*/true);
|
||||
void CreateJsepTransportPair(SrtpMode mode) {
|
||||
jsep_transport1_ = CreateJsepTransport2(/*rtcp_mux_enabled=*/true, mode);
|
||||
jsep_transport2_ = CreateJsepTransport2(/*rtcp_mux_enabled=*/true, mode);
|
||||
|
||||
auto fake_dtls1 =
|
||||
static_cast<FakeDtlsTransport*>(jsep_transport1_->rtp_dtls_transport());
|
||||
@ -968,12 +1069,14 @@ class JsepTransport2HeaderExtensionTest
|
||||
fake_dtls2->fake_ice_transport()->SignalReadPacket.connect(
|
||||
this, &JsepTransport2HeaderExtensionTest::OnReadPacket2);
|
||||
|
||||
auto cert1 = rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
|
||||
jsep_transport1_->rtp_dtls_transport()->SetLocalCertificate(cert1);
|
||||
auto cert2 = rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
|
||||
jsep_transport2_->rtp_dtls_transport()->SetLocalCertificate(cert2);
|
||||
if (mode == SrtpMode::kDtlsSrtp) {
|
||||
auto cert1 = rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
|
||||
jsep_transport1_->rtp_dtls_transport()->SetLocalCertificate(cert1);
|
||||
auto cert2 = rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
|
||||
jsep_transport2_->rtp_dtls_transport()->SetLocalCertificate(cert2);
|
||||
}
|
||||
}
|
||||
|
||||
void OnReadPacket1(rtc::PacketTransportInternal* transport,
|
||||
@ -1060,10 +1163,17 @@ class JsepTransport2HeaderExtensionTest
|
||||
TEST_P(JsepTransport2HeaderExtensionTest, EncryptedHeaderExtensionNegotiation) {
|
||||
Scenario scenario = std::get<0>(GetParam());
|
||||
bool use_gcm = std::get<1>(GetParam());
|
||||
CreateJsepTransportPair();
|
||||
SrtpMode mode = SrtpMode ::kDtlsSrtp;
|
||||
if (scenario == Scenario::kSdes) {
|
||||
mode = SrtpMode::kSdes;
|
||||
}
|
||||
CreateJsepTransportPair(mode);
|
||||
recv_encrypted_headers1_.push_back(kHeaderExtensionIDs[0]);
|
||||
recv_encrypted_headers2_.push_back(kHeaderExtensionIDs[1]);
|
||||
|
||||
cricket::CryptoParams sdes_param(1, rtc::kCsAesCm128HmacSha1_80,
|
||||
"inline:" + rtc::CreateRandomString(40),
|
||||
std::string());
|
||||
if (use_gcm) {
|
||||
auto fake_dtls1 =
|
||||
static_cast<FakeDtlsTransport*>(jsep_transport1_->rtp_dtls_transport());
|
||||
@ -1080,6 +1190,9 @@ TEST_P(JsepTransport2HeaderExtensionTest, EncryptedHeaderExtensionNegotiation) {
|
||||
|
||||
JsepTransportDescription offer_desc;
|
||||
offer_desc.encrypted_header_extension_ids = recv_encrypted_headers1_;
|
||||
if (scenario == Scenario::kSdes) {
|
||||
offer_desc.cryptos.push_back(sdes_param);
|
||||
}
|
||||
ASSERT_TRUE(
|
||||
jsep_transport1_
|
||||
->SetLocalJsepTransportDescription(offer_desc, SdpType::kOffer)
|
||||
@ -1091,6 +1204,9 @@ TEST_P(JsepTransport2HeaderExtensionTest, EncryptedHeaderExtensionNegotiation) {
|
||||
|
||||
JsepTransportDescription answer_desc;
|
||||
answer_desc.encrypted_header_extension_ids = recv_encrypted_headers2_;
|
||||
if (scenario == Scenario::kSdes) {
|
||||
answer_desc.cryptos.push_back(sdes_param);
|
||||
}
|
||||
ASSERT_TRUE(
|
||||
jsep_transport2_
|
||||
->SetLocalJsepTransportDescription(answer_desc, SdpType::kAnswer)
|
||||
@ -1109,7 +1225,8 @@ TEST_P(JsepTransport2HeaderExtensionTest, EncryptedHeaderExtensionNegotiation) {
|
||||
->SetRemoteJsepTransportDescription(answer_desc, SdpType::kAnswer)
|
||||
.ok());
|
||||
|
||||
if (scenario == Scenario::kDtlsAfterCallerSetAnswer) {
|
||||
if (scenario == Scenario::kDtlsAfterCallerSetAnswer ||
|
||||
scenario == Scenario::kSdes) {
|
||||
ConnectTransport();
|
||||
}
|
||||
EXPECT_TRUE(jsep_transport1_->rtp_transport()->IsSrtpActive());
|
||||
@ -1148,6 +1265,7 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
JsepTransport2Test,
|
||||
JsepTransport2HeaderExtensionTest,
|
||||
::testing::Values(
|
||||
std::make_tuple(Scenario::kSdes, false),
|
||||
std::make_tuple(Scenario::kDtlsBeforeCallerSendOffer, true),
|
||||
std::make_tuple(Scenario::kDtlsBeforeCallerSetAnswer, true),
|
||||
std::make_tuple(Scenario::kDtlsAfterCallerSetAnswer, true),
|
||||
@ -1157,7 +1275,8 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
|
||||
// This test verifies the ICE parameters are properly applied to the transports.
|
||||
TEST_F(JsepTransport2Test, SetIceParametersWithRenomination) {
|
||||
jsep_transport_ = CreateJsepTransport2(/* rtcp_mux_enabled= */ true);
|
||||
jsep_transport_ =
|
||||
CreateJsepTransport2(/* rtcp_mux_enabled= */ true, SrtpMode::kDtlsSrtp);
|
||||
|
||||
JsepTransportDescription jsep_description;
|
||||
jsep_description.transport_desc = TransportDescription(kIceUfrag1, kIcePwd1);
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/crypto_params.h"
|
||||
#include "api/video_codecs/h264_profile_level_id.h"
|
||||
#include "media/base/codec.h"
|
||||
#include "media/base/media_constants.h"
|
||||
@ -47,6 +48,19 @@ namespace {
|
||||
using rtc::UniqueRandomIdGenerator;
|
||||
using webrtc::RtpTransceiverDirection;
|
||||
|
||||
const char kInline[] = "inline:";
|
||||
|
||||
void GetSupportedSdesCryptoSuiteNames(
|
||||
void (*func)(const webrtc::CryptoOptions&, std::vector<int>*),
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<std::string>* names) {
|
||||
std::vector<int> crypto_suites;
|
||||
func(crypto_options, &crypto_suites);
|
||||
for (const auto crypto : crypto_suites) {
|
||||
names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
|
||||
}
|
||||
}
|
||||
|
||||
webrtc::RtpExtension RtpExtensionFromCapability(
|
||||
const webrtc::RtpHeaderExtensionCapability& capability) {
|
||||
return webrtc::RtpExtension(capability.uri,
|
||||
@ -121,6 +135,157 @@ static bool IsMediaContentOfType(const ContentInfo* content,
|
||||
return content->media_description()->type() == media_type;
|
||||
}
|
||||
|
||||
static bool CreateCryptoParams(int tag,
|
||||
const std::string& cipher,
|
||||
CryptoParams* crypto_out) {
|
||||
int key_len;
|
||||
int salt_len;
|
||||
if (!rtc::GetSrtpKeyAndSaltLengths(rtc::SrtpCryptoSuiteFromName(cipher),
|
||||
&key_len, &salt_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int master_key_len = key_len + salt_len;
|
||||
std::string master_key;
|
||||
if (!rtc::CreateRandomData(master_key_len, &master_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RTC_CHECK_EQ(master_key_len, master_key.size());
|
||||
std::string key = rtc::Base64::Encode(master_key);
|
||||
|
||||
crypto_out->tag = tag;
|
||||
crypto_out->cipher_suite = cipher;
|
||||
crypto_out->key_params = kInline;
|
||||
crypto_out->key_params += key;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool AddCryptoParams(const std::string& cipher_suite,
|
||||
CryptoParamsVec* cryptos_out) {
|
||||
int size = static_cast<int>(cryptos_out->size());
|
||||
|
||||
cryptos_out->resize(size + 1);
|
||||
return CreateCryptoParams(size, cipher_suite, &cryptos_out->at(size));
|
||||
}
|
||||
|
||||
void AddMediaCryptos(const CryptoParamsVec& cryptos,
|
||||
MediaContentDescription* media) {
|
||||
for (const CryptoParams& crypto : cryptos) {
|
||||
media->AddCrypto(crypto);
|
||||
}
|
||||
}
|
||||
|
||||
bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
|
||||
MediaContentDescription* media) {
|
||||
CryptoParamsVec cryptos;
|
||||
for (const std::string& crypto_suite : crypto_suites) {
|
||||
if (!AddCryptoParams(crypto_suite, &cryptos)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
AddMediaCryptos(cryptos, media);
|
||||
return true;
|
||||
}
|
||||
|
||||
const CryptoParamsVec* GetCryptos(const ContentInfo* content) {
|
||||
if (!content || !content->media_description()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &content->media_description()->cryptos();
|
||||
}
|
||||
|
||||
bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
|
||||
const CryptoParams& crypto,
|
||||
CryptoParams* crypto_out) {
|
||||
auto it = absl::c_find_if(
|
||||
cryptos, [&crypto](const CryptoParams& c) { return crypto.Matches(c); });
|
||||
if (it == cryptos.end()) {
|
||||
return false;
|
||||
}
|
||||
*crypto_out = *it;
|
||||
return true;
|
||||
}
|
||||
|
||||
// For audio, HMAC 32 (if enabled) is prefered over HMAC 80 because of the
|
||||
// low overhead.
|
||||
void GetSupportedAudioSdesCryptoSuites(
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<int>* crypto_suites) {
|
||||
if (crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher) {
|
||||
crypto_suites->push_back(rtc::kSrtpAes128CmSha1_32);
|
||||
}
|
||||
crypto_suites->push_back(rtc::kSrtpAes128CmSha1_80);
|
||||
if (crypto_options.srtp.enable_gcm_crypto_suites) {
|
||||
crypto_suites->push_back(rtc::kSrtpAeadAes256Gcm);
|
||||
crypto_suites->push_back(rtc::kSrtpAeadAes128Gcm);
|
||||
}
|
||||
}
|
||||
|
||||
void GetSupportedAudioSdesCryptoSuiteNames(
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<std::string>* crypto_suite_names) {
|
||||
GetSupportedSdesCryptoSuiteNames(GetSupportedAudioSdesCryptoSuites,
|
||||
crypto_options, crypto_suite_names);
|
||||
}
|
||||
|
||||
void GetSupportedVideoSdesCryptoSuites(
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<int>* crypto_suites) {
|
||||
crypto_suites->push_back(rtc::kSrtpAes128CmSha1_80);
|
||||
if (crypto_options.srtp.enable_gcm_crypto_suites) {
|
||||
crypto_suites->push_back(rtc::kSrtpAeadAes256Gcm);
|
||||
crypto_suites->push_back(rtc::kSrtpAeadAes128Gcm);
|
||||
}
|
||||
}
|
||||
|
||||
void GetSupportedVideoSdesCryptoSuiteNames(
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<std::string>* crypto_suite_names) {
|
||||
GetSupportedSdesCryptoSuiteNames(GetSupportedVideoSdesCryptoSuites,
|
||||
crypto_options, crypto_suite_names);
|
||||
}
|
||||
|
||||
void GetSupportedDataSdesCryptoSuites(
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<int>* crypto_suites) {
|
||||
crypto_suites->push_back(rtc::kSrtpAes128CmSha1_80);
|
||||
if (crypto_options.srtp.enable_gcm_crypto_suites) {
|
||||
crypto_suites->push_back(rtc::kSrtpAeadAes256Gcm);
|
||||
crypto_suites->push_back(rtc::kSrtpAeadAes128Gcm);
|
||||
}
|
||||
}
|
||||
|
||||
void GetSupportedDataSdesCryptoSuiteNames(
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<std::string>* crypto_suite_names) {
|
||||
GetSupportedSdesCryptoSuiteNames(GetSupportedDataSdesCryptoSuites,
|
||||
crypto_options, crypto_suite_names);
|
||||
}
|
||||
|
||||
// Support any GCM cipher (if enabled through options). For video support only
|
||||
// 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated (if enabled) unless
|
||||
// bundle is enabled because it is low overhead.
|
||||
// Pick the crypto in the list that is supported.
|
||||
static bool SelectCrypto(const MediaContentDescription* offer,
|
||||
bool bundle,
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
CryptoParams* crypto_out) {
|
||||
bool audio = offer->type() == MEDIA_TYPE_AUDIO;
|
||||
const CryptoParamsVec& cryptos = offer->cryptos();
|
||||
|
||||
for (const CryptoParams& crypto : cryptos) {
|
||||
if ((crypto_options.srtp.enable_gcm_crypto_suites &&
|
||||
rtc::IsGcmCryptoSuiteName(crypto.cipher_suite)) ||
|
||||
rtc::kCsAesCm128HmacSha1_80 == crypto.cipher_suite ||
|
||||
(rtc::kCsAesCm128HmacSha1_32 == crypto.cipher_suite && audio &&
|
||||
!bundle && crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher)) {
|
||||
return CreateCryptoParams(crypto.tag, crypto.cipher_suite, crypto_out);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finds all StreamParams of all media types and attach them to stream_params.
|
||||
static StreamParamsVec GetCurrentStreamParams(
|
||||
const std::vector<const ContentInfo*>& active_local_contents) {
|
||||
@ -318,6 +483,119 @@ static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Gets the CryptoParamsVec of the given `content_name` from `sdesc`, and
|
||||
// sets it to `cryptos`.
|
||||
static bool GetCryptosByName(const SessionDescription* sdesc,
|
||||
const std::string& content_name,
|
||||
CryptoParamsVec* cryptos) {
|
||||
if (!sdesc || !cryptos) {
|
||||
return false;
|
||||
}
|
||||
const ContentInfo* content = sdesc->GetContentByName(content_name);
|
||||
if (!content || !content->media_description()) {
|
||||
return false;
|
||||
}
|
||||
*cryptos = content->media_description()->cryptos();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prunes the `target_cryptos` by removing the crypto params (cipher_suite)
|
||||
// which are not available in `filter`.
|
||||
static void PruneCryptos(const CryptoParamsVec& filter,
|
||||
CryptoParamsVec* target_cryptos) {
|
||||
if (!target_cryptos) {
|
||||
return;
|
||||
}
|
||||
|
||||
target_cryptos->erase(
|
||||
std::remove_if(target_cryptos->begin(), target_cryptos->end(),
|
||||
// Returns true if the `crypto`'s cipher_suite is not
|
||||
// found in `filter`.
|
||||
[&filter](const CryptoParams& crypto) {
|
||||
for (const CryptoParams& entry : filter) {
|
||||
if (entry.cipher_suite == crypto.cipher_suite)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
target_cryptos->end());
|
||||
}
|
||||
|
||||
static bool IsRtpContent(SessionDescription* sdesc,
|
||||
const std::string& content_name) {
|
||||
bool is_rtp = false;
|
||||
ContentInfo* content = sdesc->GetContentByName(content_name);
|
||||
if (content && content->media_description()) {
|
||||
is_rtp = IsRtpProtocol(content->media_description()->protocol());
|
||||
}
|
||||
return is_rtp;
|
||||
}
|
||||
|
||||
// Updates the crypto parameters of the `sdesc` according to the given
|
||||
// `bundle_group`. The crypto parameters of all the contents within the
|
||||
// `bundle_group` should be updated to use the common subset of the
|
||||
// available cryptos.
|
||||
static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
|
||||
SessionDescription* sdesc) {
|
||||
// The bundle should not be empty.
|
||||
if (!sdesc || !bundle_group.FirstContentName()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool common_cryptos_needed = false;
|
||||
// Get the common cryptos.
|
||||
const ContentNames& content_names = bundle_group.content_names();
|
||||
CryptoParamsVec common_cryptos;
|
||||
bool first = true;
|
||||
for (const std::string& content_name : content_names) {
|
||||
if (!IsRtpContent(sdesc, content_name)) {
|
||||
continue;
|
||||
}
|
||||
// The common cryptos are needed if any of the content does not have DTLS
|
||||
// enabled.
|
||||
if (!sdesc->GetTransportInfoByName(content_name)->description.secure()) {
|
||||
common_cryptos_needed = true;
|
||||
}
|
||||
if (first) {
|
||||
first = false;
|
||||
// Initial the common_cryptos with the first content in the bundle group.
|
||||
if (!GetCryptosByName(sdesc, content_name, &common_cryptos)) {
|
||||
return false;
|
||||
}
|
||||
if (common_cryptos.empty()) {
|
||||
// If there's no crypto params, we should just return.
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
CryptoParamsVec cryptos;
|
||||
if (!GetCryptosByName(sdesc, content_name, &cryptos)) {
|
||||
return false;
|
||||
}
|
||||
PruneCryptos(cryptos, &common_cryptos);
|
||||
}
|
||||
}
|
||||
|
||||
if (common_cryptos.empty() && common_cryptos_needed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update to use the common cryptos.
|
||||
for (const std::string& content_name : content_names) {
|
||||
if (!IsRtpContent(sdesc, content_name)) {
|
||||
continue;
|
||||
}
|
||||
ContentInfo* content = sdesc->GetContentByName(content_name);
|
||||
if (IsMediaContent(content)) {
|
||||
MediaContentDescription* media_desc = content->media_description();
|
||||
if (!media_desc) {
|
||||
return false;
|
||||
}
|
||||
media_desc->set_cryptos(common_cryptos);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::vector<const ContentInfo*> GetActiveContents(
|
||||
const SessionDescription& description,
|
||||
const MediaSessionOptions& session_options) {
|
||||
@ -371,11 +649,17 @@ static bool IsFlexfecCodec(const C& codec) {
|
||||
}
|
||||
|
||||
// Create a media content to be offered for the given `sender_options`,
|
||||
// according to the given parameters.
|
||||
// The created content is added to the offer.
|
||||
// according to the given options.rtcp_mux, session_options.is_muc, codecs,
|
||||
// secure_transport, crypto, and current_streams. If we don't currently have
|
||||
// crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
|
||||
// created (according to crypto_suites). The created content is added to the
|
||||
// offer.
|
||||
static bool CreateContentOffer(
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const SecurePolicy& secure_policy,
|
||||
const CryptoParamsVec* current_cryptos,
|
||||
const std::vector<std::string>& crypto_suites,
|
||||
const RtpHeaderExtensions& rtp_extensions,
|
||||
UniqueRandomIdGenerator* ssrc_generator,
|
||||
StreamParamsVec* current_streams,
|
||||
@ -402,6 +686,20 @@ static bool CreateContentOffer(
|
||||
|
||||
AddSimulcastToMediaDescription(media_description_options, offer);
|
||||
|
||||
if (secure_policy != SEC_DISABLED) {
|
||||
if (current_cryptos) {
|
||||
AddMediaCryptos(*current_cryptos, offer);
|
||||
}
|
||||
if (offer->cryptos().empty()) {
|
||||
if (!CreateMediaCryptos(crypto_suites, offer)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
template <class C>
|
||||
@ -409,6 +707,9 @@ static bool CreateMediaContentOffer(
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const std::vector<C>& codecs,
|
||||
const SecurePolicy& secure_policy,
|
||||
const CryptoParamsVec* current_cryptos,
|
||||
const std::vector<std::string>& crypto_suites,
|
||||
const RtpHeaderExtensions& rtp_extensions,
|
||||
UniqueRandomIdGenerator* ssrc_generator,
|
||||
StreamParamsVec* current_streams,
|
||||
@ -421,6 +722,7 @@ static bool CreateMediaContentOffer(
|
||||
}
|
||||
|
||||
return CreateContentOffer(media_description_options, session_options,
|
||||
secure_policy, current_cryptos, crypto_suites,
|
||||
rtp_extensions, ssrc_generator, current_streams,
|
||||
offer);
|
||||
}
|
||||
@ -1042,13 +1344,17 @@ static bool SetCodecsInAnswer(
|
||||
|
||||
// Create a media content to be answered for the given `sender_options`
|
||||
// according to the given session_options.rtcp_mux, session_options.streams,
|
||||
// codecs, and current_streams. The codecs and rtcp_mux are all
|
||||
// codecs, crypto, and current_streams. If we don't currently have crypto (in
|
||||
// current_cryptos) and it is enabled (in secure_policy), crypto is created
|
||||
// (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
|
||||
// negotiated with the offer. If the negotiation fails, this method returns
|
||||
// false. The created content is added to the offer.
|
||||
static bool CreateMediaContentAnswer(
|
||||
const MediaContentDescription* offer,
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const SecurePolicy& sdes_policy,
|
||||
const CryptoParamsVec* current_cryptos,
|
||||
const RtpHeaderExtensions& local_rtp_extensions,
|
||||
UniqueRandomIdGenerator* ssrc_generator,
|
||||
bool enable_encrypted_rtp_header_extensions,
|
||||
@ -1073,6 +1379,21 @@ static bool CreateMediaContentAnswer(
|
||||
|
||||
answer->set_remote_estimate(offer->remote_estimate());
|
||||
|
||||
if (sdes_policy != SEC_DISABLED) {
|
||||
CryptoParams crypto;
|
||||
if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
|
||||
&crypto)) {
|
||||
if (current_cryptos) {
|
||||
FindMatchingCrypto(*current_cryptos, crypto, &crypto);
|
||||
}
|
||||
answer->AddCrypto(crypto);
|
||||
}
|
||||
}
|
||||
|
||||
if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AddSimulcastToMediaDescription(media_description_options, answer);
|
||||
|
||||
answer->set_direction(NegotiateRtpTransceiverDirection(
|
||||
@ -1112,7 +1433,9 @@ static bool IsMediaProtocolSupported(MediaType type,
|
||||
|
||||
static void SetMediaProtocol(bool secure_transport,
|
||||
MediaContentDescription* desc) {
|
||||
if (secure_transport)
|
||||
if (!desc->cryptos().empty())
|
||||
desc->set_protocol(kMediaProtocolSavpf);
|
||||
else if (secure_transport)
|
||||
desc->set_protocol(kMediaProtocolDtlsSavpf);
|
||||
else
|
||||
desc->set_protocol(kMediaProtocolAvpf);
|
||||
@ -1134,6 +1457,23 @@ static const TransportDescription* GetTransportDescription(
|
||||
return desc;
|
||||
}
|
||||
|
||||
// Gets the current DTLS state from the transport description.
|
||||
static bool IsDtlsActive(const ContentInfo* content,
|
||||
const SessionDescription* current_description) {
|
||||
if (!content) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t msection_index = content - ¤t_description->contents()[0];
|
||||
|
||||
if (current_description->transport_infos().size() <= msection_index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return current_description->transport_infos()[msection_index]
|
||||
.description.secure();
|
||||
}
|
||||
|
||||
void MediaDescriptionOptions::AddAudioSender(
|
||||
const std::string& track_id,
|
||||
const std::vector<std::string>& stream_ids) {
|
||||
@ -1372,6 +1712,11 @@ std::unique_ptr<SessionDescription> MediaSessionDescriptionFactory::CreateOffer(
|
||||
<< "CreateOffer failed to UpdateTransportInfoForBundle.";
|
||||
return nullptr;
|
||||
}
|
||||
if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
|
||||
RTC_LOG(LS_ERROR)
|
||||
<< "CreateOffer failed to UpdateCryptoParamsForBundle.";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1462,21 +1807,16 @@ MediaSessionDescriptionFactory::CreateAnswer(
|
||||
RTC_DCHECK(media_description_options.mid == offer_content->name);
|
||||
// Get the index of the BUNDLE group that this MID belongs to, if any.
|
||||
absl::optional<size_t> bundle_index;
|
||||
bool require_transport_attributes = true;
|
||||
for (size_t i = 0; i < offer_bundles.size(); ++i) {
|
||||
if (offer_bundles[i]->HasContentName(media_description_options.mid)) {
|
||||
bundle_index = i;
|
||||
if (offer_bundles[i]->FirstContentName() &&
|
||||
*offer_bundles[i]->FirstContentName() !=
|
||||
media_description_options.mid) {
|
||||
require_transport_attributes = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
TransportInfo* bundle_transport =
|
||||
bundle_index.has_value() ? bundle_transports[bundle_index.value()].get()
|
||||
: nullptr;
|
||||
|
||||
const ContentInfo* current_content = nullptr;
|
||||
if (current_description &&
|
||||
msection_index < current_description->contents().size()) {
|
||||
@ -1490,9 +1830,8 @@ MediaSessionDescriptionFactory::CreateAnswer(
|
||||
if (!AddAudioContentForAnswer(
|
||||
media_description_options, session_options, offer_content,
|
||||
offer, current_content, current_description, bundle_transport,
|
||||
require_transport_attributes, answer_audio_codecs,
|
||||
header_extensions, ¤t_streams, answer.get(),
|
||||
&ice_credentials)) {
|
||||
answer_audio_codecs, header_extensions, ¤t_streams,
|
||||
answer.get(), &ice_credentials)) {
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
@ -1500,9 +1839,8 @@ MediaSessionDescriptionFactory::CreateAnswer(
|
||||
if (!AddVideoContentForAnswer(
|
||||
media_description_options, session_options, offer_content,
|
||||
offer, current_content, current_description, bundle_transport,
|
||||
require_transport_attributes, answer_video_codecs,
|
||||
header_extensions, ¤t_streams, answer.get(),
|
||||
&ice_credentials)) {
|
||||
answer_video_codecs, header_extensions, ¤t_streams,
|
||||
answer.get(), &ice_credentials)) {
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
@ -1510,8 +1848,7 @@ MediaSessionDescriptionFactory::CreateAnswer(
|
||||
if (!AddDataContentForAnswer(
|
||||
media_description_options, session_options, offer_content,
|
||||
offer, current_content, current_description, bundle_transport,
|
||||
require_transport_attributes, ¤t_streams, answer.get(),
|
||||
&ice_credentials)) {
|
||||
¤t_streams, answer.get(), &ice_credentials)) {
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
@ -1519,7 +1856,7 @@ MediaSessionDescriptionFactory::CreateAnswer(
|
||||
if (!AddUnsupportedContentForAnswer(
|
||||
media_description_options, session_options, offer_content,
|
||||
offer, current_content, current_description, bundle_transport,
|
||||
require_transport_attributes, answer.get(), &ice_credentials)) {
|
||||
answer.get(), &ice_credentials)) {
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
@ -1558,6 +1895,12 @@ MediaSessionDescriptionFactory::CreateAnswer(
|
||||
<< "CreateAnswer failed to UpdateTransportInfoForBundle.";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
|
||||
RTC_LOG(LS_ERROR)
|
||||
<< "CreateAnswer failed to UpdateCryptoParamsForBundle.";
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1951,14 +2294,23 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
|
||||
StripCNCodecs(&filtered_codecs);
|
||||
}
|
||||
|
||||
cricket::SecurePolicy sdes_policy =
|
||||
IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
|
||||
: secure();
|
||||
|
||||
auto audio = std::make_unique<AudioContentDescription>();
|
||||
std::vector<std::string> crypto_suites;
|
||||
GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
|
||||
&crypto_suites);
|
||||
if (!CreateMediaContentOffer(media_description_options, session_options,
|
||||
filtered_codecs, audio_rtp_extensions,
|
||||
ssrc_generator_, current_streams, audio.get())) {
|
||||
filtered_codecs, sdes_policy,
|
||||
GetCryptos(current_content), crypto_suites,
|
||||
audio_rtp_extensions, ssrc_generator_,
|
||||
current_streams, audio.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool secure_transport = transport_desc_factory_->IsEncrypted();
|
||||
bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
|
||||
SetMediaProtocol(secure_transport, audio.get());
|
||||
|
||||
audio->set_direction(media_description_options.direction);
|
||||
@ -2036,16 +2388,24 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
|
||||
}
|
||||
}
|
||||
|
||||
cricket::SecurePolicy sdes_policy =
|
||||
IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
|
||||
: secure();
|
||||
auto video = std::make_unique<VideoContentDescription>();
|
||||
std::vector<std::string> crypto_suites;
|
||||
GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
|
||||
&crypto_suites);
|
||||
if (!CreateMediaContentOffer(media_description_options, session_options,
|
||||
filtered_codecs, video_rtp_extensions,
|
||||
ssrc_generator_, current_streams, video.get())) {
|
||||
filtered_codecs, sdes_policy,
|
||||
GetCryptos(current_content), crypto_suites,
|
||||
video_rtp_extensions, ssrc_generator_,
|
||||
current_streams, video.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
video->set_bandwidth(kAutoBandwidth);
|
||||
|
||||
bool secure_transport = transport_desc_factory_->IsEncrypted();
|
||||
bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
|
||||
SetMediaProtocol(secure_transport, video.get());
|
||||
|
||||
video->set_direction(media_description_options.direction);
|
||||
@ -2071,8 +2431,15 @@ bool MediaSessionDescriptionFactory::AddDataContentForOffer(
|
||||
IceCredentialsIterator* ice_credentials) const {
|
||||
auto data = std::make_unique<SctpDataContentDescription>();
|
||||
|
||||
bool secure_transport = transport_desc_factory_->IsEncrypted();
|
||||
bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
|
||||
|
||||
cricket::SecurePolicy sdes_policy =
|
||||
IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
|
||||
: secure();
|
||||
std::vector<std::string> crypto_suites;
|
||||
// SDES doesn't make sense for SCTP, so we disable it, and we only
|
||||
// get SDES crypto suites for RTP-based data channels.
|
||||
sdes_policy = cricket::SEC_DISABLED;
|
||||
// Unlike SetMediaProtocol below, we need to set the protocol
|
||||
// before we call CreateMediaContentOffer. Otherwise,
|
||||
// CreateMediaContentOffer won't know this is SCTP and will
|
||||
@ -2083,7 +2450,8 @@ bool MediaSessionDescriptionFactory::AddDataContentForOffer(
|
||||
data->set_max_message_size(kSctpSendBufferSize);
|
||||
|
||||
if (!CreateContentOffer(media_description_options, session_options,
|
||||
RtpHeaderExtensions(), ssrc_generator_,
|
||||
sdes_policy, GetCryptos(current_content),
|
||||
crypto_suites, RtpHeaderExtensions(), ssrc_generator_,
|
||||
current_streams, data.get())) {
|
||||
return false;
|
||||
}
|
||||
@ -2143,7 +2511,6 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
bool require_transport_attributes,
|
||||
const AudioCodecs& audio_codecs,
|
||||
const RtpHeaderExtensions& default_audio_rtp_header_extensions,
|
||||
StreamParamsVec* current_streams,
|
||||
@ -2156,7 +2523,7 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
|
||||
std::unique_ptr<TransportDescription> audio_transport = CreateTransportAnswer(
|
||||
media_description_options.mid, offer_description,
|
||||
media_description_options.transport_options, current_description,
|
||||
require_transport_attributes, ice_credentials);
|
||||
bundle_transport != nullptr, ice_credentials);
|
||||
if (!audio_transport) {
|
||||
return false;
|
||||
}
|
||||
@ -2211,6 +2578,9 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
|
||||
bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
|
||||
session_options.bundle_enabled;
|
||||
auto audio_answer = std::make_unique<AudioContentDescription>();
|
||||
// Do not require or create SDES cryptos if DTLS is used.
|
||||
cricket::SecurePolicy sdes_policy =
|
||||
audio_transport->secure() ? cricket::SEC_DISABLED : secure();
|
||||
if (!SetCodecsInAnswer(offer_audio_description, filtered_codecs,
|
||||
media_description_options, session_options,
|
||||
ssrc_generator_, current_streams,
|
||||
@ -2219,6 +2589,7 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
|
||||
}
|
||||
if (!CreateMediaContentAnswer(
|
||||
offer_audio_description, media_description_options, session_options,
|
||||
sdes_policy, GetCryptos(current_content),
|
||||
filtered_rtp_header_extensions(default_audio_rtp_header_extensions),
|
||||
ssrc_generator_, enable_encrypted_rtp_header_extensions_,
|
||||
current_streams, bundle_enabled, audio_answer.get())) {
|
||||
@ -2256,7 +2627,6 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
bool require_transport_attributes,
|
||||
const VideoCodecs& video_codecs,
|
||||
const RtpHeaderExtensions& default_video_rtp_header_extensions,
|
||||
StreamParamsVec* current_streams,
|
||||
@ -2269,7 +2639,7 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
|
||||
std::unique_ptr<TransportDescription> video_transport = CreateTransportAnswer(
|
||||
media_description_options.mid, offer_description,
|
||||
media_description_options.transport_options, current_description,
|
||||
require_transport_attributes, ice_credentials);
|
||||
bundle_transport != nullptr, ice_credentials);
|
||||
if (!video_transport) {
|
||||
return false;
|
||||
}
|
||||
@ -2328,6 +2698,9 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
|
||||
bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
|
||||
session_options.bundle_enabled;
|
||||
auto video_answer = std::make_unique<VideoContentDescription>();
|
||||
// Do not require or create SDES cryptos if DTLS is used.
|
||||
cricket::SecurePolicy sdes_policy =
|
||||
video_transport->secure() ? cricket::SEC_DISABLED : secure();
|
||||
if (!SetCodecsInAnswer(offer_video_description, filtered_codecs,
|
||||
media_description_options, session_options,
|
||||
ssrc_generator_, current_streams,
|
||||
@ -2336,6 +2709,7 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
|
||||
}
|
||||
if (!CreateMediaContentAnswer(
|
||||
offer_video_description, media_description_options, session_options,
|
||||
sdes_policy, GetCryptos(current_content),
|
||||
filtered_rtp_header_extensions(default_video_rtp_header_extensions),
|
||||
ssrc_generator_, enable_encrypted_rtp_header_extensions_,
|
||||
current_streams, bundle_enabled, video_answer.get())) {
|
||||
@ -2371,18 +2745,20 @@ bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
bool require_transport_attributes,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* answer,
|
||||
IceCredentialsIterator* ice_credentials) const {
|
||||
std::unique_ptr<TransportDescription> data_transport = CreateTransportAnswer(
|
||||
media_description_options.mid, offer_description,
|
||||
media_description_options.transport_options, current_description,
|
||||
require_transport_attributes, ice_credentials);
|
||||
bundle_transport != nullptr, ice_credentials);
|
||||
if (!data_transport) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do not require or create SDES cryptos if DTLS is used.
|
||||
cricket::SecurePolicy sdes_policy =
|
||||
data_transport->secure() ? cricket::SEC_DISABLED : secure();
|
||||
bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
|
||||
session_options.bundle_enabled;
|
||||
RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_DATA));
|
||||
@ -2407,9 +2783,9 @@ bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
|
||||
}
|
||||
if (!CreateMediaContentAnswer(
|
||||
offer_data_description, media_description_options, session_options,
|
||||
RtpHeaderExtensions(), ssrc_generator_,
|
||||
enable_encrypted_rtp_header_extensions_, current_streams,
|
||||
bundle_enabled, data_answer.get())) {
|
||||
sdes_policy, GetCryptos(current_content), RtpHeaderExtensions(),
|
||||
ssrc_generator_, enable_encrypted_rtp_header_extensions_,
|
||||
current_streams, bundle_enabled, data_answer.get())) {
|
||||
return false; // Fails the session setup.
|
||||
}
|
||||
// Respond with sctpmap if the offer uses sctpmap.
|
||||
@ -2444,13 +2820,12 @@ bool MediaSessionDescriptionFactory::AddUnsupportedContentForAnswer(
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
bool require_transport_attributes,
|
||||
SessionDescription* answer,
|
||||
IceCredentialsIterator* ice_credentials) const {
|
||||
std::unique_ptr<TransportDescription> unsupported_transport =
|
||||
CreateTransportAnswer(media_description_options.mid, offer_description,
|
||||
media_description_options.transport_options,
|
||||
current_description, require_transport_attributes,
|
||||
current_description, bundle_transport != nullptr,
|
||||
ice_credentials);
|
||||
if (!unsupported_transport) {
|
||||
return false;
|
||||
|
||||
@ -156,6 +156,8 @@ class MediaSessionDescriptionFactory {
|
||||
const VideoCodecs& recv_codecs);
|
||||
RtpHeaderExtensions filtered_rtp_header_extensions(
|
||||
RtpHeaderExtensions extensions) const;
|
||||
SecurePolicy secure() const { return secure_; }
|
||||
void set_secure(SecurePolicy s) { secure_ = s; }
|
||||
|
||||
void set_enable_encrypted_rtp_header_extensions(bool enable) {
|
||||
enable_encrypted_rtp_header_extensions_ = enable;
|
||||
@ -272,7 +274,6 @@ class MediaSessionDescriptionFactory {
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
bool require_transport_attributes,
|
||||
const AudioCodecs& audio_codecs,
|
||||
const RtpHeaderExtensions& default_audio_rtp_header_extensions,
|
||||
StreamParamsVec* current_streams,
|
||||
@ -287,7 +288,6 @@ class MediaSessionDescriptionFactory {
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
bool require_transport_attributes,
|
||||
const VideoCodecs& video_codecs,
|
||||
const RtpHeaderExtensions& default_video_rtp_header_extensions,
|
||||
StreamParamsVec* current_streams,
|
||||
@ -302,7 +302,6 @@ class MediaSessionDescriptionFactory {
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
bool require_transport_attributes,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* answer,
|
||||
IceCredentialsIterator* ice_credentials) const;
|
||||
@ -315,7 +314,6 @@ class MediaSessionDescriptionFactory {
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
bool require_transport_attributes,
|
||||
SessionDescription* answer,
|
||||
IceCredentialsIterator* ice_credentials) const;
|
||||
|
||||
@ -339,6 +337,9 @@ class MediaSessionDescriptionFactory {
|
||||
// This object is not owned by the channel so it must outlive it.
|
||||
rtc::UniqueRandomIdGenerator* const ssrc_generator_;
|
||||
bool enable_encrypted_rtp_header_extensions_ = false;
|
||||
// TODO(zhihuang): Rename secure_ to sdec_policy_; rename the related getter
|
||||
// and setter.
|
||||
SecurePolicy secure_ = SEC_DISABLED;
|
||||
const TransportDescriptionFactory* transport_desc_factory_;
|
||||
};
|
||||
|
||||
@ -382,6 +383,26 @@ VideoContentDescription* GetFirstVideoContentDescription(
|
||||
SctpDataContentDescription* GetFirstSctpDataContentDescription(
|
||||
SessionDescription* sdesc);
|
||||
|
||||
// Helper functions to return crypto suites used for SDES.
|
||||
void GetSupportedAudioSdesCryptoSuites(
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<int>* crypto_suites);
|
||||
void GetSupportedVideoSdesCryptoSuites(
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<int>* crypto_suites);
|
||||
void GetSupportedDataSdesCryptoSuites(
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<int>* crypto_suites);
|
||||
void GetSupportedAudioSdesCryptoSuiteNames(
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<std::string>* crypto_suite_names);
|
||||
void GetSupportedVideoSdesCryptoSuiteNames(
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<std::string>* crypto_suite_names);
|
||||
void GetSupportedDataSdesCryptoSuiteNames(
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
std::vector<std::string>* crypto_suite_names);
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // PC_MEDIA_SESSION_H_
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "p2p/base/transport_info.h"
|
||||
#include "pc/rtp_media_utils.h"
|
||||
#include "pc/srtp_filter.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/fake_ssl_identity.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
@ -36,11 +37,16 @@
|
||||
#include "test/field_trial.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
#define ASSERT_CRYPTO(cd, s, cs) \
|
||||
ASSERT_EQ(s, cd->cryptos().size()); \
|
||||
ASSERT_EQ(cs, cd->cryptos()[0].cipher_suite)
|
||||
|
||||
typedef std::vector<cricket::Candidate> Candidates;
|
||||
|
||||
using cricket::AudioCodec;
|
||||
using cricket::AudioContentDescription;
|
||||
using cricket::ContentInfo;
|
||||
using cricket::CryptoParamsVec;
|
||||
using cricket::GetFirstAudioContent;
|
||||
using cricket::GetFirstAudioContentDescription;
|
||||
using cricket::GetFirstDataContent;
|
||||
@ -59,6 +65,9 @@ using cricket::MediaType;
|
||||
using cricket::RidDescription;
|
||||
using cricket::RidDirection;
|
||||
using cricket::SctpDataContentDescription;
|
||||
using cricket::SEC_DISABLED;
|
||||
using cricket::SEC_ENABLED;
|
||||
using cricket::SEC_REQUIRED;
|
||||
using cricket::SessionDescription;
|
||||
using cricket::SimulcastDescription;
|
||||
using cricket::SimulcastLayer;
|
||||
@ -256,6 +265,11 @@ static const char* kMediaProtocolsDtls[] = {
|
||||
"TCP/TLS/RTP/SAVPF", "TCP/TLS/RTP/SAVP", "UDP/TLS/RTP/SAVPF",
|
||||
"UDP/TLS/RTP/SAVP"};
|
||||
|
||||
// SRTP cipher name negotiated by the tests. This must be updated if the
|
||||
// default changes.
|
||||
static const char* kDefaultSrtpCryptoSuite = kCsAesCm128HmacSha1_80;
|
||||
static const char* kDefaultSrtpCryptoSuiteGcm = kCsAeadAes256Gcm;
|
||||
|
||||
// These constants are used to make the code using "AddMediaDescriptionOptions"
|
||||
// more readable.
|
||||
static constexpr bool kStopped = true;
|
||||
@ -389,6 +403,17 @@ static MediaSessionOptions CreatePlanBMediaSessionOptions() {
|
||||
return session_options;
|
||||
}
|
||||
|
||||
// prefers GCM SDES crypto suites by removing non-GCM defaults.
|
||||
void PreferGcmCryptoParameters(CryptoParamsVec* cryptos) {
|
||||
cryptos->erase(
|
||||
std::remove_if(cryptos->begin(), cryptos->end(),
|
||||
[](const cricket::CryptoParams& crypto) {
|
||||
return crypto.cipher_suite != kCsAeadAes256Gcm &&
|
||||
crypto.cipher_suite != kCsAeadAes128Gcm;
|
||||
}),
|
||||
cryptos->end());
|
||||
}
|
||||
|
||||
// TODO(zhihuang): Most of these tests were written while MediaSessionOptions
|
||||
// was designed for Plan B SDP, where only one audio "m=" section and one video
|
||||
// "m=" section could be generated, and ordering couldn't be controlled. Many of
|
||||
@ -405,7 +430,10 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
|
||||
MAKE_VECTOR(kAudioCodecs2));
|
||||
f2_.set_video_codecs(MAKE_VECTOR(kVideoCodecs2),
|
||||
MAKE_VECTOR(kVideoCodecs2));
|
||||
SetDtls(true);
|
||||
tdf1_.set_certificate(rtc::RTCCertificate::Create(
|
||||
std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id1"))));
|
||||
tdf2_.set_certificate(rtc::RTCCertificate::Create(
|
||||
std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id2"))));
|
||||
}
|
||||
|
||||
// Create a video StreamParamsVec object with:
|
||||
@ -435,6 +463,18 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
|
||||
return video_streams;
|
||||
}
|
||||
|
||||
bool CompareCryptoParams(const CryptoParamsVec& c1,
|
||||
const CryptoParamsVec& c2) {
|
||||
if (c1.size() != c2.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < c1.size(); ++i)
|
||||
if (c1[i].tag != c2[i].tag || c1[i].cipher_suite != c2[i].cipher_suite ||
|
||||
c1[i].key_params != c2[i].key_params ||
|
||||
c1[i].session_params != c2[i].session_params)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true if the transport info contains "renomination" as an
|
||||
// ICE option.
|
||||
bool GetIceRenomination(const TransportInfo* transport_info) {
|
||||
@ -547,6 +587,50 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
|
||||
}
|
||||
}
|
||||
|
||||
void TestCryptoWithBundle(bool offer) {
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
MediaSessionOptions options;
|
||||
AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
|
||||
std::unique_ptr<SessionDescription> ref_desc;
|
||||
std::unique_ptr<SessionDescription> desc;
|
||||
if (offer) {
|
||||
options.bundle_enabled = false;
|
||||
ref_desc = f1_.CreateOffer(options, NULL);
|
||||
options.bundle_enabled = true;
|
||||
desc = f1_.CreateOffer(options, ref_desc.get());
|
||||
} else {
|
||||
options.bundle_enabled = true;
|
||||
ref_desc = f1_.CreateOffer(options, NULL);
|
||||
desc = f1_.CreateAnswer(ref_desc.get(), options, NULL);
|
||||
}
|
||||
ASSERT_TRUE(desc);
|
||||
const cricket::MediaContentDescription* audio_media_desc =
|
||||
desc->GetContentDescriptionByName("audio");
|
||||
ASSERT_TRUE(audio_media_desc);
|
||||
const cricket::MediaContentDescription* video_media_desc =
|
||||
desc->GetContentDescriptionByName("video");
|
||||
ASSERT_TRUE(video_media_desc);
|
||||
EXPECT_TRUE(CompareCryptoParams(audio_media_desc->cryptos(),
|
||||
video_media_desc->cryptos()));
|
||||
EXPECT_EQ(1u, audio_media_desc->cryptos().size());
|
||||
EXPECT_EQ(kDefaultSrtpCryptoSuite,
|
||||
audio_media_desc->cryptos()[0].cipher_suite);
|
||||
|
||||
// Verify the selected crypto is one from the reference audio
|
||||
// media content.
|
||||
const cricket::MediaContentDescription* ref_audio_media_desc =
|
||||
ref_desc->GetContentDescriptionByName("audio");
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < ref_audio_media_desc->cryptos().size(); ++i) {
|
||||
if (ref_audio_media_desc->cryptos()[i].Matches(
|
||||
audio_media_desc->cryptos()[0])) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found);
|
||||
}
|
||||
|
||||
// This test that the audio and video media direction is set to
|
||||
// `expected_direction_in_answer` in an answer if the offer direction is set
|
||||
// to `direction_in_offer` and the answer is willing to both send and receive.
|
||||
@ -590,6 +674,59 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestVideoGcmCipher(bool gcm_offer, bool gcm_answer) {
|
||||
MediaSessionOptions offer_opts;
|
||||
AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &offer_opts);
|
||||
offer_opts.crypto_options.srtp.enable_gcm_crypto_suites = gcm_offer;
|
||||
|
||||
MediaSessionOptions answer_opts;
|
||||
AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &answer_opts);
|
||||
answer_opts.crypto_options.srtp.enable_gcm_crypto_suites = gcm_answer;
|
||||
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
std::unique_ptr<SessionDescription> offer =
|
||||
f1_.CreateOffer(offer_opts, NULL);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
if (gcm_offer && gcm_answer) {
|
||||
for (cricket::ContentInfo& content : offer->contents()) {
|
||||
auto cryptos = content.media_description()->cryptos();
|
||||
PreferGcmCryptoParameters(&cryptos);
|
||||
content.media_description()->set_cryptos(cryptos);
|
||||
}
|
||||
}
|
||||
std::unique_ptr<SessionDescription> answer =
|
||||
f2_.CreateAnswer(offer.get(), answer_opts, NULL);
|
||||
const ContentInfo* ac = answer->GetContentByName("audio");
|
||||
const ContentInfo* vc = answer->GetContentByName("video");
|
||||
ASSERT_TRUE(ac != NULL);
|
||||
ASSERT_TRUE(vc != NULL);
|
||||
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
|
||||
EXPECT_EQ(MediaProtocolType::kRtp, vc->type);
|
||||
const AudioContentDescription* acd = ac->media_description()->as_audio();
|
||||
const VideoContentDescription* vcd = vc->media_description()->as_video();
|
||||
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
|
||||
EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
|
||||
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
|
||||
EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
|
||||
EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
|
||||
if (gcm_offer && gcm_answer) {
|
||||
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuiteGcm);
|
||||
} else {
|
||||
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
|
||||
}
|
||||
EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
|
||||
EXPECT_THAT(vcd->codecs(), ElementsAreArray(kVideoCodecsAnswer));
|
||||
EXPECT_EQ(0U, vcd->first_ssrc()); // no sender is attached
|
||||
EXPECT_TRUE(vcd->rtcp_mux()); // negotiated rtcp-mux
|
||||
if (gcm_offer && gcm_answer) {
|
||||
ASSERT_CRYPTO(vcd, 1U, kDefaultSrtpCryptoSuiteGcm);
|
||||
} else {
|
||||
ASSERT_CRYPTO(vcd, 1U, kDefaultSrtpCryptoSuite);
|
||||
}
|
||||
EXPECT_EQ(cricket::kMediaProtocolSavpf, vcd->protocol());
|
||||
}
|
||||
|
||||
void TestTransportSequenceNumberNegotiation(
|
||||
const cricket::RtpHeaderExtensions& local,
|
||||
const cricket::RtpHeaderExtensions& offered,
|
||||
@ -644,24 +781,6 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
protected:
|
||||
// Helper to turn connection encryption with DTLS on or off.
|
||||
// Default state is on.
|
||||
void SetDtls(bool dtls_f1, bool dtls_f2) {
|
||||
if (dtls_f1) {
|
||||
tdf1_.set_certificate(rtc::RTCCertificate::Create(
|
||||
std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id1"))));
|
||||
} else {
|
||||
tdf1_.set_certificate(nullptr);
|
||||
}
|
||||
if (dtls_f2) {
|
||||
tdf2_.set_certificate(rtc::RTCCertificate::Create(
|
||||
std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id2"))));
|
||||
} else {
|
||||
tdf2_.set_certificate(nullptr);
|
||||
}
|
||||
}
|
||||
void SetDtls(bool dtls) { SetDtls(dtls, dtls); }
|
||||
|
||||
UniqueRandomIdGenerator ssrc_generator1;
|
||||
UniqueRandomIdGenerator ssrc_generator2;
|
||||
MediaSessionDescriptionFactory f1_;
|
||||
@ -672,6 +791,7 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
|
||||
|
||||
// Create a typical audio offer, and ensure it matches what we expect.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioOffer) {
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
std::unique_ptr<SessionDescription> offer =
|
||||
f1_.CreateOffer(CreatePlanBMediaSessionOptions(), NULL);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
@ -686,13 +806,15 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioOffer) {
|
||||
EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached.
|
||||
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto)
|
||||
EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on
|
||||
EXPECT_EQ(cricket::kMediaProtocolDtlsSavpf, acd->protocol());
|
||||
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
|
||||
EXPECT_EQ(cricket::kMediaProtocolSavpf, acd->protocol());
|
||||
}
|
||||
|
||||
// Create a typical video offer, and ensure it matches what we expect.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoOffer) {
|
||||
MediaSessionOptions opts;
|
||||
AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
const ContentInfo* ac = offer->GetContentByName("audio");
|
||||
@ -708,13 +830,15 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoOffer) {
|
||||
EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
|
||||
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto)
|
||||
EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on
|
||||
EXPECT_EQ(cricket::kMediaProtocolDtlsSavpf, acd->protocol());
|
||||
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
|
||||
EXPECT_EQ(cricket::kMediaProtocolSavpf, acd->protocol());
|
||||
EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
|
||||
EXPECT_EQ(f1_.video_sendrecv_codecs(), vcd->codecs());
|
||||
EXPECT_EQ(0U, vcd->first_ssrc()); // no sender is attached
|
||||
EXPECT_EQ(kAutoBandwidth, vcd->bandwidth()); // default bandwidth (auto)
|
||||
EXPECT_TRUE(vcd->rtcp_mux()); // rtcp-mux defaults on
|
||||
EXPECT_EQ(cricket::kMediaProtocolDtlsSavpf, vcd->protocol());
|
||||
ASSERT_CRYPTO(vcd, 1U, kDefaultSrtpCryptoSuite);
|
||||
EXPECT_EQ(cricket::kMediaProtocolSavpf, vcd->protocol());
|
||||
}
|
||||
|
||||
// Test creating an offer with bundle where the Codecs have the same dynamic
|
||||
@ -744,6 +868,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestBundleOfferWithSameCodecPlType) {
|
||||
// after an audio only session has been negotiated.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
TestCreateUpdatedVideoOfferWithBundle) {
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
MediaSessionOptions opts;
|
||||
AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
|
||||
RtpTransceiverDirection::kRecvOnly, kActive,
|
||||
@ -769,16 +895,18 @@ TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
EXPECT_TRUE(NULL != vcd);
|
||||
EXPECT_TRUE(NULL != acd);
|
||||
|
||||
EXPECT_EQ(cricket::kMediaProtocolDtlsSavpf, acd->protocol());
|
||||
EXPECT_EQ(cricket::kMediaProtocolDtlsSavpf, vcd->protocol());
|
||||
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
|
||||
EXPECT_EQ(cricket::kMediaProtocolSavpf, acd->protocol());
|
||||
ASSERT_CRYPTO(vcd, 1U, kDefaultSrtpCryptoSuite);
|
||||
EXPECT_EQ(cricket::kMediaProtocolSavpf, vcd->protocol());
|
||||
}
|
||||
|
||||
// Create an SCTP data offer with bundle without error.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSctpDataOffer) {
|
||||
SetDtls(false);
|
||||
MediaSessionOptions opts;
|
||||
opts.bundle_enabled = true;
|
||||
AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
|
||||
EXPECT_TRUE(offer.get() != NULL);
|
||||
EXPECT_TRUE(offer->GetContentByName("data") != NULL);
|
||||
@ -793,6 +921,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSecureSctpDataOffer) {
|
||||
MediaSessionOptions opts;
|
||||
opts.bundle_enabled = true;
|
||||
AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
tdf1_.set_secure(SEC_ENABLED);
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
|
||||
EXPECT_TRUE(offer.get() != NULL);
|
||||
EXPECT_TRUE(offer->GetContentByName("data") != NULL);
|
||||
@ -804,10 +934,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSecureSctpDataOffer) {
|
||||
|
||||
// Test creating an sctp data channel from an already generated offer.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateImplicitSctpDataOffer) {
|
||||
SetDtls(false);
|
||||
MediaSessionOptions opts;
|
||||
opts.bundle_enabled = true;
|
||||
AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
std::unique_ptr<SessionDescription> offer1(f1_.CreateOffer(opts, NULL));
|
||||
ASSERT_TRUE(offer1.get() != NULL);
|
||||
const ContentInfo* data = offer1->GetContentByName("data");
|
||||
@ -1108,6 +1238,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateOfferContentOrder) {
|
||||
|
||||
// Create a typical audio answer, and ensure it matches what we expect.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswer) {
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
std::unique_ptr<SessionDescription> offer =
|
||||
f1_.CreateOffer(CreatePlanBMediaSessionOptions(), NULL);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
@ -1124,13 +1256,47 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswer) {
|
||||
EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
|
||||
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
|
||||
EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
|
||||
EXPECT_EQ(cricket::kMediaProtocolDtlsSavpf, acd->protocol());
|
||||
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
|
||||
EXPECT_EQ(cricket::kMediaProtocolSavpf, acd->protocol());
|
||||
}
|
||||
|
||||
// Create a typical audio answer with GCM ciphers enabled, and ensure it
|
||||
// matches what we expect.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswerGcm) {
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
MediaSessionOptions opts = CreatePlanBMediaSessionOptions();
|
||||
opts.crypto_options.srtp.enable_gcm_crypto_suites = true;
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
for (cricket::ContentInfo& content : offer->contents()) {
|
||||
auto cryptos = content.media_description()->cryptos();
|
||||
PreferGcmCryptoParameters(&cryptos);
|
||||
content.media_description()->set_cryptos(cryptos);
|
||||
}
|
||||
std::unique_ptr<SessionDescription> answer =
|
||||
f2_.CreateAnswer(offer.get(), opts, NULL);
|
||||
const ContentInfo* ac = answer->GetContentByName("audio");
|
||||
const ContentInfo* vc = answer->GetContentByName("video");
|
||||
ASSERT_TRUE(ac != NULL);
|
||||
ASSERT_TRUE(vc == NULL);
|
||||
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
|
||||
const AudioContentDescription* acd = ac->media_description()->as_audio();
|
||||
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
|
||||
EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
|
||||
EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
|
||||
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
|
||||
EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
|
||||
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuiteGcm);
|
||||
EXPECT_EQ(cricket::kMediaProtocolSavpf, acd->protocol());
|
||||
}
|
||||
|
||||
// Create a typical video answer, and ensure it matches what we expect.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswer) {
|
||||
MediaSessionOptions opts;
|
||||
AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
std::unique_ptr<SessionDescription> answer =
|
||||
@ -1148,11 +1314,31 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswer) {
|
||||
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
|
||||
EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
|
||||
EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
|
||||
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
|
||||
EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
|
||||
EXPECT_THAT(vcd->codecs(), ElementsAreArray(kVideoCodecsAnswer));
|
||||
EXPECT_EQ(0U, vcd->first_ssrc()); // no sender is attached
|
||||
EXPECT_TRUE(vcd->rtcp_mux()); // negotiated rtcp-mux
|
||||
EXPECT_EQ(cricket::kMediaProtocolDtlsSavpf, vcd->protocol());
|
||||
ASSERT_CRYPTO(vcd, 1U, kDefaultSrtpCryptoSuite);
|
||||
EXPECT_EQ(cricket::kMediaProtocolSavpf, vcd->protocol());
|
||||
}
|
||||
|
||||
// Create a typical video answer with GCM ciphers enabled, and ensure it
|
||||
// matches what we expect.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerGcm) {
|
||||
TestVideoGcmCipher(true, true);
|
||||
}
|
||||
|
||||
// Create a typical video answer with GCM ciphers enabled for the offer only,
|
||||
// and ensure it matches what we expect.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerGcmOffer) {
|
||||
TestVideoGcmCipher(true, false);
|
||||
}
|
||||
|
||||
// Create a typical video answer with GCM ciphers enabled for the answer only,
|
||||
// and ensure it matches what we expect.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerGcmAnswer) {
|
||||
TestVideoGcmCipher(false, true);
|
||||
}
|
||||
|
||||
// The use_sctpmap flag should be set in an Sctp DataContentDescription by
|
||||
@ -1202,6 +1388,13 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswerWithoutSctpmap) {
|
||||
// and "TCP/DTLS/SCTP" offers.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
TestCreateDataAnswerToDifferentOfferedProtos) {
|
||||
// Need to enable DTLS offer/answer generation (disabled by default in this
|
||||
// test).
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
tdf1_.set_secure(SEC_ENABLED);
|
||||
tdf2_.set_secure(SEC_ENABLED);
|
||||
|
||||
MediaSessionOptions opts;
|
||||
AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
|
||||
@ -1229,6 +1422,13 @@ TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
|
||||
TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
TestCreateDataAnswerToOfferWithDefinedMessageSize) {
|
||||
// Need to enable DTLS offer/answer generation (disabled by default in this
|
||||
// test).
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
tdf1_.set_secure(SEC_ENABLED);
|
||||
tdf2_.set_secure(SEC_ENABLED);
|
||||
|
||||
MediaSessionOptions opts;
|
||||
AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
|
||||
@ -1251,6 +1451,13 @@ TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
|
||||
TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
TestCreateDataAnswerToOfferWithZeroMessageSize) {
|
||||
// Need to enable DTLS offer/answer generation (disabled by default in this
|
||||
// test).
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
tdf1_.set_secure(SEC_ENABLED);
|
||||
tdf2_.set_secure(SEC_ENABLED);
|
||||
|
||||
MediaSessionOptions opts;
|
||||
AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
|
||||
@ -1337,10 +1544,13 @@ TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerToInactiveOffer) {
|
||||
RtpTransceiverDirection::kInactive);
|
||||
}
|
||||
|
||||
// Test that the media protocol is RTP/AVPF if DTLS is disabled.
|
||||
// Test that the media protocol is RTP/AVPF if DTLS and SDES are disabled.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, AudioOfferAnswerWithCryptoDisabled) {
|
||||
MediaSessionOptions opts = CreatePlanBMediaSessionOptions();
|
||||
SetDtls(false);
|
||||
f1_.set_secure(SEC_DISABLED);
|
||||
f2_.set_secure(SEC_DISABLED);
|
||||
tdf1_.set_secure(SEC_DISABLED);
|
||||
tdf2_.set_secure(SEC_DISABLED);
|
||||
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
|
||||
const AudioContentDescription* offer_acd =
|
||||
@ -2102,6 +2312,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) {
|
||||
AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack2,
|
||||
{kMediaStream1}, 1, &opts);
|
||||
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
|
||||
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
@ -2126,9 +2337,11 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) {
|
||||
|
||||
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto)
|
||||
EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on
|
||||
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
|
||||
|
||||
EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
|
||||
EXPECT_EQ(f1_.video_sendrecv_codecs(), vcd->codecs());
|
||||
ASSERT_CRYPTO(vcd, 1U, kDefaultSrtpCryptoSuite);
|
||||
|
||||
const StreamParamsVec& video_streams = vcd->streams();
|
||||
ASSERT_EQ(1U, video_streams.size());
|
||||
@ -2161,6 +2374,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) {
|
||||
EXPECT_EQ(acd->codecs(), updated_acd->codecs());
|
||||
EXPECT_EQ(vcd->type(), updated_vcd->type());
|
||||
EXPECT_EQ(vcd->codecs(), updated_vcd->codecs());
|
||||
ASSERT_CRYPTO(updated_acd, 1U, kDefaultSrtpCryptoSuite);
|
||||
EXPECT_TRUE(CompareCryptoParams(acd->cryptos(), updated_acd->cryptos()));
|
||||
ASSERT_CRYPTO(updated_vcd, 1U, kDefaultSrtpCryptoSuite);
|
||||
EXPECT_TRUE(CompareCryptoParams(vcd->cryptos(), updated_vcd->cryptos()));
|
||||
|
||||
const StreamParamsVec& updated_audio_streams = updated_acd->streams();
|
||||
ASSERT_EQ(2U, updated_audio_streams.size());
|
||||
@ -2378,6 +2595,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoAnswer) {
|
||||
AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
|
||||
RtpTransceiverDirection::kRecvOnly, kActive,
|
||||
&offer_opts);
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(offer_opts, NULL);
|
||||
|
||||
MediaSessionOptions answer_opts;
|
||||
@ -2404,6 +2623,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoAnswer) {
|
||||
ASSERT_TRUE(vc != NULL);
|
||||
const AudioContentDescription* acd = ac->media_description()->as_audio();
|
||||
const VideoContentDescription* vcd = vc->media_description()->as_video();
|
||||
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
|
||||
ASSERT_CRYPTO(vcd, 1U, kDefaultSrtpCryptoSuite);
|
||||
|
||||
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
|
||||
EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
|
||||
@ -2449,6 +2670,11 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoAnswer) {
|
||||
const VideoContentDescription* updated_vcd =
|
||||
vc->media_description()->as_video();
|
||||
|
||||
ASSERT_CRYPTO(updated_acd, 1U, kDefaultSrtpCryptoSuite);
|
||||
EXPECT_TRUE(CompareCryptoParams(acd->cryptos(), updated_acd->cryptos()));
|
||||
ASSERT_CRYPTO(updated_vcd, 1U, kDefaultSrtpCryptoSuite);
|
||||
EXPECT_TRUE(CompareCryptoParams(vcd->cryptos(), updated_vcd->cryptos()));
|
||||
|
||||
EXPECT_EQ(acd->type(), updated_acd->type());
|
||||
EXPECT_EQ(acd->codecs(), updated_acd->codecs());
|
||||
EXPECT_EQ(vcd->type(), updated_vcd->type());
|
||||
@ -3380,11 +3606,27 @@ TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
TestTransportInfo(false, options, true);
|
||||
}
|
||||
|
||||
// Create an offer with bundle enabled and verify the crypto parameters are
|
||||
// the common set of the available cryptos.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoWithOfferBundle) {
|
||||
TestCryptoWithBundle(true);
|
||||
}
|
||||
|
||||
// Create an answer with bundle enabled and verify the crypto parameters are
|
||||
// the common set of the available cryptos.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoWithAnswerBundle) {
|
||||
TestCryptoWithBundle(false);
|
||||
}
|
||||
|
||||
// Verifies that creating answer fails if the offer has UDP/TLS/RTP/SAVPF but
|
||||
// DTLS is not enabled locally.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
TestOfferDtlsSavpfWithoutDtlsFailed) {
|
||||
SetDtls(true, false);
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
tdf1_.set_secure(SEC_DISABLED);
|
||||
tdf2_.set_secure(SEC_DISABLED);
|
||||
|
||||
std::unique_ptr<SessionDescription> offer =
|
||||
f1_.CreateOffer(CreatePlanBMediaSessionOptions(), NULL);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
@ -3406,6 +3648,11 @@ TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
// Offers UDP/TLS/RTP/SAVPF and verifies the answer can be created and contains
|
||||
// UDP/TLS/RTP/SAVPF.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestOfferDtlsSavpfCreateAnswer) {
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
tdf1_.set_secure(SEC_ENABLED);
|
||||
tdf2_.set_secure(SEC_ENABLED);
|
||||
|
||||
std::unique_ptr<SessionDescription> offer =
|
||||
f1_.CreateOffer(CreatePlanBMediaSessionOptions(), NULL);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
@ -3428,9 +3675,120 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestOfferDtlsSavpfCreateAnswer) {
|
||||
EXPECT_EQ(cricket::kMediaProtocolDtlsSavpf, answer_audio_desc->protocol());
|
||||
}
|
||||
|
||||
// Test that we include both SDES and DTLS in the offer, but only include SDES
|
||||
// in the answer if DTLS isn't negotiated.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoDtls) {
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
tdf1_.set_secure(SEC_ENABLED);
|
||||
tdf2_.set_secure(SEC_DISABLED);
|
||||
MediaSessionOptions options;
|
||||
AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
|
||||
std::unique_ptr<SessionDescription> offer, answer;
|
||||
const cricket::MediaContentDescription* audio_media_desc;
|
||||
const cricket::MediaContentDescription* video_media_desc;
|
||||
const cricket::TransportDescription* audio_trans_desc;
|
||||
const cricket::TransportDescription* video_trans_desc;
|
||||
|
||||
// Generate an offer with SDES and DTLS support.
|
||||
offer = f1_.CreateOffer(options, NULL);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
|
||||
audio_media_desc = offer->GetContentDescriptionByName("audio");
|
||||
ASSERT_TRUE(audio_media_desc != NULL);
|
||||
video_media_desc = offer->GetContentDescriptionByName("video");
|
||||
ASSERT_TRUE(video_media_desc != NULL);
|
||||
EXPECT_EQ(1u, audio_media_desc->cryptos().size());
|
||||
EXPECT_EQ(1u, video_media_desc->cryptos().size());
|
||||
|
||||
audio_trans_desc = offer->GetTransportDescriptionByName("audio");
|
||||
ASSERT_TRUE(audio_trans_desc != NULL);
|
||||
video_trans_desc = offer->GetTransportDescriptionByName("video");
|
||||
ASSERT_TRUE(video_trans_desc != NULL);
|
||||
ASSERT_TRUE(audio_trans_desc->identity_fingerprint.get() != NULL);
|
||||
ASSERT_TRUE(video_trans_desc->identity_fingerprint.get() != NULL);
|
||||
|
||||
// Generate an answer with only SDES support, since tdf2 has crypto disabled.
|
||||
answer = f2_.CreateAnswer(offer.get(), options, NULL);
|
||||
ASSERT_TRUE(answer.get() != NULL);
|
||||
|
||||
audio_media_desc = answer->GetContentDescriptionByName("audio");
|
||||
ASSERT_TRUE(audio_media_desc != NULL);
|
||||
video_media_desc = answer->GetContentDescriptionByName("video");
|
||||
ASSERT_TRUE(video_media_desc != NULL);
|
||||
EXPECT_EQ(1u, audio_media_desc->cryptos().size());
|
||||
EXPECT_EQ(1u, video_media_desc->cryptos().size());
|
||||
|
||||
audio_trans_desc = answer->GetTransportDescriptionByName("audio");
|
||||
ASSERT_TRUE(audio_trans_desc != NULL);
|
||||
video_trans_desc = answer->GetTransportDescriptionByName("video");
|
||||
ASSERT_TRUE(video_trans_desc != NULL);
|
||||
ASSERT_TRUE(audio_trans_desc->identity_fingerprint.get() == NULL);
|
||||
ASSERT_TRUE(video_trans_desc->identity_fingerprint.get() == NULL);
|
||||
|
||||
// Enable DTLS; the answer should now only have DTLS support.
|
||||
tdf2_.set_secure(SEC_ENABLED);
|
||||
answer = f2_.CreateAnswer(offer.get(), options, NULL);
|
||||
ASSERT_TRUE(answer.get() != NULL);
|
||||
|
||||
audio_media_desc = answer->GetContentDescriptionByName("audio");
|
||||
ASSERT_TRUE(audio_media_desc != NULL);
|
||||
video_media_desc = answer->GetContentDescriptionByName("video");
|
||||
ASSERT_TRUE(video_media_desc != NULL);
|
||||
EXPECT_TRUE(audio_media_desc->cryptos().empty());
|
||||
EXPECT_TRUE(video_media_desc->cryptos().empty());
|
||||
EXPECT_EQ(cricket::kMediaProtocolSavpf, audio_media_desc->protocol());
|
||||
EXPECT_EQ(cricket::kMediaProtocolSavpf, video_media_desc->protocol());
|
||||
|
||||
audio_trans_desc = answer->GetTransportDescriptionByName("audio");
|
||||
ASSERT_TRUE(audio_trans_desc != NULL);
|
||||
video_trans_desc = answer->GetTransportDescriptionByName("video");
|
||||
ASSERT_TRUE(video_trans_desc != NULL);
|
||||
ASSERT_TRUE(audio_trans_desc->identity_fingerprint.get() != NULL);
|
||||
ASSERT_TRUE(video_trans_desc->identity_fingerprint.get() != NULL);
|
||||
|
||||
// Try creating offer again. DTLS enabled now, crypto's should be empty
|
||||
// in new offer.
|
||||
offer = f1_.CreateOffer(options, offer.get());
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
audio_media_desc = offer->GetContentDescriptionByName("audio");
|
||||
ASSERT_TRUE(audio_media_desc != NULL);
|
||||
video_media_desc = offer->GetContentDescriptionByName("video");
|
||||
ASSERT_TRUE(video_media_desc != NULL);
|
||||
EXPECT_TRUE(audio_media_desc->cryptos().empty());
|
||||
EXPECT_TRUE(video_media_desc->cryptos().empty());
|
||||
|
||||
audio_trans_desc = offer->GetTransportDescriptionByName("audio");
|
||||
ASSERT_TRUE(audio_trans_desc != NULL);
|
||||
video_trans_desc = offer->GetTransportDescriptionByName("video");
|
||||
ASSERT_TRUE(video_trans_desc != NULL);
|
||||
ASSERT_TRUE(audio_trans_desc->identity_fingerprint.get() != NULL);
|
||||
ASSERT_TRUE(video_trans_desc->identity_fingerprint.get() != NULL);
|
||||
}
|
||||
|
||||
// Test that an answer can't be created if cryptos are required but the offer is
|
||||
// unsecure.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestSecureAnswerToUnsecureOffer) {
|
||||
MediaSessionOptions options = CreatePlanBMediaSessionOptions();
|
||||
f1_.set_secure(SEC_DISABLED);
|
||||
tdf1_.set_secure(SEC_DISABLED);
|
||||
f2_.set_secure(SEC_REQUIRED);
|
||||
tdf1_.set_secure(SEC_ENABLED);
|
||||
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(options, NULL);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
std::unique_ptr<SessionDescription> answer =
|
||||
f2_.CreateAnswer(offer.get(), options, NULL);
|
||||
EXPECT_TRUE(answer.get() == NULL);
|
||||
}
|
||||
|
||||
// Test that we accept a DTLS offer without SDES and create an appropriate
|
||||
// answer.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoOfferDtlsButNotSdes) {
|
||||
f1_.set_secure(SEC_DISABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
tdf1_.set_secure(SEC_ENABLED);
|
||||
tdf2_.set_secure(SEC_ENABLED);
|
||||
MediaSessionOptions options;
|
||||
AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
|
||||
|
||||
@ -3438,6 +3796,13 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoOfferDtlsButNotSdes) {
|
||||
std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(options, NULL);
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
|
||||
const AudioContentDescription* audio_offer =
|
||||
GetFirstAudioContentDescription(offer.get());
|
||||
ASSERT_TRUE(audio_offer->cryptos().empty());
|
||||
const VideoContentDescription* video_offer =
|
||||
GetFirstVideoContentDescription(offer.get());
|
||||
ASSERT_TRUE(video_offer->cryptos().empty());
|
||||
|
||||
const cricket::TransportDescription* audio_offer_trans_desc =
|
||||
offer->GetTransportDescriptionByName("audio");
|
||||
ASSERT_TRUE(audio_offer_trans_desc->identity_fingerprint.get() != NULL);
|
||||
@ -3964,10 +4329,14 @@ class MediaProtocolTest : public ::testing::TestWithParam<const char*> {
|
||||
MAKE_VECTOR(kAudioCodecs2));
|
||||
f2_.set_video_codecs(MAKE_VECTOR(kVideoCodecs2),
|
||||
MAKE_VECTOR(kVideoCodecs2));
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
tdf1_.set_certificate(rtc::RTCCertificate::Create(
|
||||
std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id1"))));
|
||||
tdf2_.set_certificate(rtc::RTCCertificate::Create(
|
||||
std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id2"))));
|
||||
tdf1_.set_secure(SEC_ENABLED);
|
||||
tdf2_.set_secure(SEC_ENABLED);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@ -2634,7 +2634,9 @@ void PeerConnection::ReportRemoteIceCandidateAdded(
|
||||
|
||||
bool PeerConnection::SrtpRequired() const {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||
return dtls_enabled_;
|
||||
return (dtls_enabled_ ||
|
||||
sdp_handler_->webrtc_session_desc_factory()->SdesPolicy() ==
|
||||
cricket::SEC_REQUIRED);
|
||||
}
|
||||
|
||||
void PeerConnection::OnTransportControllerGatheringState(
|
||||
|
||||
@ -128,6 +128,13 @@ SdpContentPredicate HaveDtlsFingerprint() {
|
||||
};
|
||||
}
|
||||
|
||||
SdpContentPredicate HaveSdesCryptos() {
|
||||
return [](const cricket::ContentInfo* content,
|
||||
const cricket::TransportInfo* transport) {
|
||||
return !content->media_description()->cryptos().empty();
|
||||
};
|
||||
}
|
||||
|
||||
SdpContentPredicate HaveProtocol(const std::string& protocol) {
|
||||
return [protocol](const cricket::ContentInfo* content,
|
||||
const cricket::TransportInfo* transport) {
|
||||
@ -135,6 +142,22 @@ SdpContentPredicate HaveProtocol(const std::string& protocol) {
|
||||
};
|
||||
}
|
||||
|
||||
SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
|
||||
return [num_crypto_suites](const cricket::ContentInfo* content,
|
||||
const cricket::TransportInfo* transport) {
|
||||
const auto& cryptos = content->media_description()->cryptos();
|
||||
if (cryptos.size() != num_crypto_suites) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < cryptos.size(); ++i) {
|
||||
if (cryptos[i].key_params.size() == 67U &&
|
||||
cryptos[i].cipher_suite == "AEAD_AES_256_GCM")
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
class PeerConnectionCryptoTest
|
||||
: public PeerConnectionCryptoBaseTest,
|
||||
public ::testing::WithParamInterface<SdpSemantics> {
|
||||
@ -142,13 +165,20 @@ class PeerConnectionCryptoTest
|
||||
PeerConnectionCryptoTest() : PeerConnectionCryptoBaseTest(GetParam()) {}
|
||||
};
|
||||
|
||||
SdpContentMutator RemoveSdesCryptos() {
|
||||
return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
|
||||
content->media_description()->set_cryptos({});
|
||||
};
|
||||
}
|
||||
|
||||
SdpContentMutator RemoveDtlsFingerprint() {
|
||||
return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
|
||||
transport->description.identity_fingerprint.reset();
|
||||
};
|
||||
}
|
||||
|
||||
// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint.
|
||||
// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
|
||||
// no SDES cryptos.
|
||||
TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
|
||||
RTCConfiguration config;
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(config);
|
||||
@ -158,6 +188,7 @@ TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) {
|
||||
|
||||
ASSERT_FALSE(offer->description()->contents().empty());
|
||||
EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
|
||||
EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
|
||||
EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
|
||||
offer->description()));
|
||||
}
|
||||
@ -172,6 +203,7 @@ TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
|
||||
|
||||
ASSERT_FALSE(answer->description()->contents().empty());
|
||||
EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
|
||||
EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
|
||||
EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
|
||||
answer->description()));
|
||||
}
|
||||
@ -190,6 +222,7 @@ TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) {
|
||||
ASSERT_TRUE(offer);
|
||||
|
||||
ASSERT_FALSE(offer->description()->contents().empty());
|
||||
EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
|
||||
EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
|
||||
EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
|
||||
offer->description()));
|
||||
@ -208,6 +241,7 @@ TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) {
|
||||
ASSERT_TRUE(answer);
|
||||
|
||||
ASSERT_FALSE(answer->description()->contents().empty());
|
||||
EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
|
||||
EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
|
||||
EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
|
||||
answer->description()));
|
||||
|
||||
@ -2077,13 +2077,10 @@ TEST_P(PeerConnectionInterfaceTest, ReceiveFireFoxOffer) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Test that SDP containing both a=fingerprint and a=crypto is handled
|
||||
// by ignoring the a=crypto part.
|
||||
// Prior to 2017, such an SDP would be accepted with SDES crypto, but
|
||||
// the fallback was removed.
|
||||
// Prior to 2021, such an SDP would be rejected because of the mixture.
|
||||
// Post 2021, a=crypto lines are totally ignored by the SDP parser.
|
||||
TEST_P(PeerConnectionInterfaceTest, SdesIgnored) {
|
||||
// Test that fallback from DTLS to SDES is not supported.
|
||||
// The fallback was previously supported but was removed to simplify the code
|
||||
// and because it's non-standard.
|
||||
TEST_P(PeerConnectionInterfaceTest, DtlsSdesFallbackNotSupported) {
|
||||
RTCConfiguration rtc_config;
|
||||
CreatePeerConnection(rtc_config);
|
||||
// Wait for fake certificate to be generated. Previously, this is what caused
|
||||
@ -2096,7 +2093,7 @@ TEST_P(PeerConnectionInterfaceTest, SdesIgnored) {
|
||||
std::unique_ptr<SessionDescriptionInterface> desc(
|
||||
webrtc::CreateSessionDescription(SdpType::kOffer, kDtlsSdesFallbackSdp,
|
||||
nullptr));
|
||||
EXPECT_TRUE(DoSetSessionDescription(std::move(desc), /*local=*/false));
|
||||
EXPECT_FALSE(DoSetSessionDescription(std::move(desc), /*local=*/false));
|
||||
}
|
||||
|
||||
// Test that we can create an audio only offer and receive an answer with a
|
||||
|
||||
@ -852,9 +852,6 @@ TEST_P(PeerConnectionSignalingTest, UnsupportedContentType) {
|
||||
"s=-\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=bogus 9 FOO 0 8\r\n"
|
||||
"a=fingerprint:sha-256 "
|
||||
"D8:6C:3D:FA:23:E2:2C:63:11:2D:D0:86:BE:C4:D0:65:F9:42:F7:1C:06:04:27:E6:"
|
||||
"1C:2C:74:01:8D:50:67:23\r\n"
|
||||
"c=IN IP4 0.0.0.0\r\n"
|
||||
"a=mid:bogusmid\r\n";
|
||||
std::unique_ptr<webrtc::SessionDescriptionInterface> remote_description =
|
||||
@ -864,7 +861,6 @@ TEST_P(PeerConnectionSignalingTest, UnsupportedContentType) {
|
||||
|
||||
// Assert we respond back with something meaningful.
|
||||
auto answer = caller->CreateAnswer();
|
||||
ASSERT_TRUE(answer);
|
||||
ASSERT_EQ(answer->description()->contents().size(), 1u);
|
||||
EXPECT_NE(answer->description()
|
||||
->contents()[0]
|
||||
|
||||
@ -108,6 +108,7 @@ const char kSdpWithoutIceUfragPwd[] =
|
||||
"Called with SDP without ice-ufrag and ice-pwd.";
|
||||
const char kSdpWithoutDtlsFingerprint[] =
|
||||
"Called with SDP without DTLS fingerprint.";
|
||||
const char kSdpWithoutSdesCrypto[] = "Called with SDP without SDES crypto.";
|
||||
|
||||
const char kSessionError[] = "Session error code: ";
|
||||
const char kSessionErrorDesc[] = "Session error description: ";
|
||||
@ -345,13 +346,14 @@ bool MediaSectionsHaveSameCount(const SessionDescription& desc1,
|
||||
const SessionDescription& desc2) {
|
||||
return desc1.contents().size() == desc2.contents().size();
|
||||
}
|
||||
// Checks that each non-rejected content has a DTLS
|
||||
// Checks that each non-rejected content has SDES crypto keys or a DTLS
|
||||
// fingerprint, unless it's in a BUNDLE group, in which case only the
|
||||
// BUNDLE-tag section (first media section/description in the BUNDLE group)
|
||||
// needs a ufrag and pwd. Mismatches, such as replying with a DTLS fingerprint
|
||||
// to SDES keys, will be caught in JsepTransport negotiation, and backstopped
|
||||
// by Channel's `srtp_required` check.
|
||||
RTCError VerifyCrypto(const SessionDescription* desc,
|
||||
bool dtls_enabled,
|
||||
const std::map<std::string, const cricket::ContentGroup*>&
|
||||
bundle_groups_by_mid) {
|
||||
for (const cricket::ContentInfo& content_info : desc->contents()) {
|
||||
@ -359,8 +361,8 @@ RTCError VerifyCrypto(const SessionDescription* desc,
|
||||
continue;
|
||||
}
|
||||
// Note what media is used with each crypto protocol, for all sections.
|
||||
// We now support only DTLS, so this metric can be retired when expiring.
|
||||
NoteKeyProtocolAndMedia(webrtc::kEnumCounterKeyProtocolDtls,
|
||||
NoteKeyProtocolAndMedia(dtls_enabled ? webrtc::kEnumCounterKeyProtocolDtls
|
||||
: webrtc::kEnumCounterKeyProtocolSdes,
|
||||
content_info.media_description()->type());
|
||||
const std::string& mid = content_info.name;
|
||||
auto it = bundle_groups_by_mid.find(mid);
|
||||
@ -381,10 +383,20 @@ RTCError VerifyCrypto(const SessionDescription* desc,
|
||||
// Something is not right.
|
||||
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp);
|
||||
}
|
||||
if (!tinfo->description.identity_fingerprint) {
|
||||
RTC_LOG(LS_WARNING) << "Session description must have DTLS fingerprint";
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
kSdpWithoutDtlsFingerprint);
|
||||
if (dtls_enabled) {
|
||||
if (!tinfo->description.identity_fingerprint) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "Session description must have DTLS fingerprint if "
|
||||
"DTLS enabled.";
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
kSdpWithoutDtlsFingerprint);
|
||||
}
|
||||
} else {
|
||||
if (media->cryptos().empty()) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "Session description must have SDES when DTLS disabled.";
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutSdesCrypto);
|
||||
}
|
||||
}
|
||||
}
|
||||
return RTCError::OK();
|
||||
@ -988,6 +1000,10 @@ void SdpOfferAnswerHandler::Initialize(
|
||||
transport_controller()->SetLocalCertificate(certificate);
|
||||
});
|
||||
|
||||
if (pc_->options()->disable_encryption) {
|
||||
webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED);
|
||||
}
|
||||
|
||||
webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions(
|
||||
pc_->GetCryptoOptions().srtp.enable_encrypted_rtp_header_extensions);
|
||||
webrtc_session_desc_factory_->set_is_unified_plan(IsUnifiedPlan());
|
||||
@ -3025,9 +3041,10 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription(
|
||||
|
||||
// Verify crypto settings.
|
||||
std::string crypto_error;
|
||||
if (pc_->dtls_enabled()) {
|
||||
RTCError crypto_error =
|
||||
VerifyCrypto(sdesc->description(), bundle_groups_by_mid);
|
||||
if (webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED ||
|
||||
pc_->dtls_enabled()) {
|
||||
RTCError crypto_error = VerifyCrypto(
|
||||
sdesc->description(), pc_->dtls_enabled(), bundle_groups_by_mid);
|
||||
if (!crypto_error.ok()) {
|
||||
return crypto_error;
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "api/crypto_params.h"
|
||||
#include "api/media_types.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/rtp_transceiver_direction.h"
|
||||
@ -43,6 +44,7 @@ namespace cricket {
|
||||
|
||||
typedef std::vector<AudioCodec> AudioCodecs;
|
||||
typedef std::vector<VideoCodec> VideoCodecs;
|
||||
typedef std::vector<CryptoParams> CryptoParamsVec;
|
||||
typedef std::vector<webrtc::RtpExtension> RtpHeaderExtensions;
|
||||
|
||||
// Options to control how session descriptions are generated.
|
||||
@ -126,6 +128,14 @@ class MediaContentDescription {
|
||||
bandwidth_type_ = bandwidth_type;
|
||||
}
|
||||
|
||||
virtual const std::vector<CryptoParams>& cryptos() const { return cryptos_; }
|
||||
virtual void AddCrypto(const CryptoParams& params) {
|
||||
cryptos_.push_back(params);
|
||||
}
|
||||
virtual void set_cryptos(const std::vector<CryptoParams>& cryptos) {
|
||||
cryptos_ = cryptos;
|
||||
}
|
||||
|
||||
// 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.
|
||||
@ -249,6 +259,7 @@ class MediaContentDescription {
|
||||
int bandwidth_ = kAutoBandwidth;
|
||||
std::string bandwidth_type_ = kApplicationSpecificBandwidth;
|
||||
std::string protocol_;
|
||||
std::vector<CryptoParams> cryptos_;
|
||||
std::vector<webrtc::RtpExtension> rtp_header_extensions_;
|
||||
bool rtp_header_extensions_set_ = false;
|
||||
StreamParamsVec send_streams_;
|
||||
|
||||
280
pc/srtp_filter.cc
Normal file
280
pc/srtp_filter.cc
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "pc/srtp_filter.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/ssl_stream_adapter.h"
|
||||
#include "rtc_base/third_party/base64/base64.h"
|
||||
#include "rtc_base/zero_memory.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
SrtpFilter::SrtpFilter() {}
|
||||
|
||||
SrtpFilter::~SrtpFilter() {}
|
||||
|
||||
bool SrtpFilter::IsActive() const {
|
||||
return state_ >= ST_ACTIVE;
|
||||
}
|
||||
|
||||
bool SrtpFilter::Process(const std::vector<CryptoParams>& cryptos,
|
||||
webrtc::SdpType type,
|
||||
ContentSource source) {
|
||||
bool ret = false;
|
||||
switch (type) {
|
||||
case webrtc::SdpType::kOffer:
|
||||
ret = SetOffer(cryptos, source);
|
||||
break;
|
||||
case webrtc::SdpType::kPrAnswer:
|
||||
ret = SetProvisionalAnswer(cryptos, source);
|
||||
break;
|
||||
case webrtc::SdpType::kAnswer:
|
||||
ret = SetAnswer(cryptos, source);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SrtpFilter::SetOffer(const std::vector<CryptoParams>& offer_params,
|
||||
ContentSource source) {
|
||||
if (!ExpectOffer(source)) {
|
||||
RTC_LOG(LS_ERROR) << "Wrong state to update SRTP offer";
|
||||
return false;
|
||||
}
|
||||
return StoreParams(offer_params, source);
|
||||
}
|
||||
|
||||
bool SrtpFilter::SetAnswer(const std::vector<CryptoParams>& answer_params,
|
||||
ContentSource source) {
|
||||
return DoSetAnswer(answer_params, source, true);
|
||||
}
|
||||
|
||||
bool SrtpFilter::SetProvisionalAnswer(
|
||||
const std::vector<CryptoParams>& answer_params,
|
||||
ContentSource source) {
|
||||
return DoSetAnswer(answer_params, source, false);
|
||||
}
|
||||
|
||||
bool SrtpFilter::ExpectOffer(ContentSource source) {
|
||||
return ((state_ == ST_INIT) || (state_ == ST_ACTIVE) ||
|
||||
(state_ == ST_SENTOFFER && source == CS_LOCAL) ||
|
||||
(state_ == ST_SENTUPDATEDOFFER && source == CS_LOCAL) ||
|
||||
(state_ == ST_RECEIVEDOFFER && source == CS_REMOTE) ||
|
||||
(state_ == ST_RECEIVEDUPDATEDOFFER && source == CS_REMOTE));
|
||||
}
|
||||
|
||||
bool SrtpFilter::StoreParams(const std::vector<CryptoParams>& params,
|
||||
ContentSource source) {
|
||||
offer_params_ = params;
|
||||
if (state_ == ST_INIT) {
|
||||
state_ = (source == CS_LOCAL) ? ST_SENTOFFER : ST_RECEIVEDOFFER;
|
||||
} else if (state_ == ST_ACTIVE) {
|
||||
state_ =
|
||||
(source == CS_LOCAL) ? ST_SENTUPDATEDOFFER : ST_RECEIVEDUPDATEDOFFER;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SrtpFilter::ExpectAnswer(ContentSource source) {
|
||||
return ((state_ == ST_SENTOFFER && source == CS_REMOTE) ||
|
||||
(state_ == ST_RECEIVEDOFFER && source == CS_LOCAL) ||
|
||||
(state_ == ST_SENTUPDATEDOFFER && source == CS_REMOTE) ||
|
||||
(state_ == ST_RECEIVEDUPDATEDOFFER && source == CS_LOCAL) ||
|
||||
(state_ == ST_SENTPRANSWER_NO_CRYPTO && source == CS_LOCAL) ||
|
||||
(state_ == ST_SENTPRANSWER && source == CS_LOCAL) ||
|
||||
(state_ == ST_RECEIVEDPRANSWER_NO_CRYPTO && source == CS_REMOTE) ||
|
||||
(state_ == ST_RECEIVEDPRANSWER && source == CS_REMOTE));
|
||||
}
|
||||
|
||||
bool SrtpFilter::DoSetAnswer(const std::vector<CryptoParams>& answer_params,
|
||||
ContentSource source,
|
||||
bool final) {
|
||||
if (!ExpectAnswer(source)) {
|
||||
RTC_LOG(LS_ERROR) << "Invalid state for SRTP answer";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the answer doesn't requests crypto complete the negotiation of an
|
||||
// unencrypted session.
|
||||
// Otherwise, finalize the parameters and apply them.
|
||||
if (answer_params.empty()) {
|
||||
if (final) {
|
||||
return ResetParams();
|
||||
} else {
|
||||
// Need to wait for the final answer to decide if
|
||||
// we should go to Active state.
|
||||
state_ = (source == CS_LOCAL) ? ST_SENTPRANSWER_NO_CRYPTO
|
||||
: ST_RECEIVEDPRANSWER_NO_CRYPTO;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
CryptoParams selected_params;
|
||||
if (!NegotiateParams(answer_params, &selected_params))
|
||||
return false;
|
||||
|
||||
const CryptoParams& new_send_params =
|
||||
(source == CS_REMOTE) ? selected_params : answer_params[0];
|
||||
const CryptoParams& new_recv_params =
|
||||
(source == CS_REMOTE) ? answer_params[0] : selected_params;
|
||||
if (!ApplySendParams(new_send_params) || !ApplyRecvParams(new_recv_params)) {
|
||||
return false;
|
||||
}
|
||||
applied_send_params_ = new_send_params;
|
||||
applied_recv_params_ = new_recv_params;
|
||||
|
||||
if (final) {
|
||||
offer_params_.clear();
|
||||
state_ = ST_ACTIVE;
|
||||
} else {
|
||||
state_ = (source == CS_LOCAL) ? ST_SENTPRANSWER : ST_RECEIVEDPRANSWER;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SrtpFilter::NegotiateParams(const std::vector<CryptoParams>& answer_params,
|
||||
CryptoParams* selected_params) {
|
||||
// We're processing an accept. We should have exactly one set of params,
|
||||
// unless the offer didn't mention crypto, in which case we shouldn't be here.
|
||||
bool ret = (answer_params.size() == 1U && !offer_params_.empty());
|
||||
if (ret) {
|
||||
// We should find a match between the answer params and the offered params.
|
||||
std::vector<CryptoParams>::const_iterator it;
|
||||
for (it = offer_params_.begin(); it != offer_params_.end(); ++it) {
|
||||
if (answer_params[0].Matches(*it)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (it != offer_params_.end()) {
|
||||
*selected_params = *it;
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
RTC_LOG(LS_WARNING) << "Invalid parameters in SRTP answer";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool SrtpFilter::ResetParams() {
|
||||
offer_params_.clear();
|
||||
applied_send_params_ = CryptoParams();
|
||||
applied_recv_params_ = CryptoParams();
|
||||
send_cipher_suite_ = absl::nullopt;
|
||||
recv_cipher_suite_ = absl::nullopt;
|
||||
send_key_.Clear();
|
||||
recv_key_.Clear();
|
||||
state_ = ST_INIT;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SrtpFilter::ApplySendParams(const CryptoParams& send_params) {
|
||||
if (applied_send_params_.cipher_suite == send_params.cipher_suite &&
|
||||
applied_send_params_.key_params == send_params.key_params) {
|
||||
RTC_LOG(LS_INFO) << "Applying the same SRTP send parameters again. No-op.";
|
||||
|
||||
// We do not want to reset the ROC if the keys are the same. So just return.
|
||||
return true;
|
||||
}
|
||||
|
||||
send_cipher_suite_ = rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite);
|
||||
if (send_cipher_suite_ == rtc::kSrtpInvalidCryptoSuite) {
|
||||
RTC_LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
|
||||
" send cipher_suite "
|
||||
<< send_params.cipher_suite;
|
||||
return false;
|
||||
}
|
||||
|
||||
int send_key_len, send_salt_len;
|
||||
if (!rtc::GetSrtpKeyAndSaltLengths(*send_cipher_suite_, &send_key_len,
|
||||
&send_salt_len)) {
|
||||
RTC_LOG(LS_ERROR) << "Could not get lengths for crypto suite(s):"
|
||||
" send cipher_suite "
|
||||
<< send_params.cipher_suite;
|
||||
return false;
|
||||
}
|
||||
|
||||
send_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(send_key_len + send_salt_len);
|
||||
return ParseKeyParams(send_params.key_params, send_key_.data(),
|
||||
send_key_.size());
|
||||
}
|
||||
|
||||
bool SrtpFilter::ApplyRecvParams(const CryptoParams& recv_params) {
|
||||
if (applied_recv_params_.cipher_suite == recv_params.cipher_suite &&
|
||||
applied_recv_params_.key_params == recv_params.key_params) {
|
||||
RTC_LOG(LS_INFO) << "Applying the same SRTP recv parameters again. No-op.";
|
||||
|
||||
// We do not want to reset the ROC if the keys are the same. So just return.
|
||||
return true;
|
||||
}
|
||||
|
||||
recv_cipher_suite_ = rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite);
|
||||
if (recv_cipher_suite_ == rtc::kSrtpInvalidCryptoSuite) {
|
||||
RTC_LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
|
||||
" recv cipher_suite "
|
||||
<< recv_params.cipher_suite;
|
||||
return false;
|
||||
}
|
||||
|
||||
int recv_key_len, recv_salt_len;
|
||||
if (!rtc::GetSrtpKeyAndSaltLengths(*recv_cipher_suite_, &recv_key_len,
|
||||
&recv_salt_len)) {
|
||||
RTC_LOG(LS_ERROR) << "Could not get lengths for crypto suite(s):"
|
||||
" recv cipher_suite "
|
||||
<< recv_params.cipher_suite;
|
||||
return false;
|
||||
}
|
||||
|
||||
recv_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(recv_key_len + recv_salt_len);
|
||||
return ParseKeyParams(recv_params.key_params, recv_key_.data(),
|
||||
recv_key_.size());
|
||||
}
|
||||
|
||||
bool SrtpFilter::ParseKeyParams(const std::string& key_params,
|
||||
uint8_t* key,
|
||||
size_t len) {
|
||||
// example key_params: "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2"
|
||||
|
||||
// Fail if key-method is wrong.
|
||||
if (!absl::StartsWith(key_params, "inline:")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail if base64 decode fails, or the key is the wrong size.
|
||||
std::string key_b64(key_params.substr(7)), key_str;
|
||||
if (!rtc::Base64::Decode(key_b64, rtc::Base64::DO_STRICT, &key_str,
|
||||
nullptr) ||
|
||||
key_str.size() != len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(key, key_str.c_str(), len);
|
||||
// TODO(bugs.webrtc.org/8905): Switch to ZeroOnFreeBuffer for storing
|
||||
// sensitive data.
|
||||
rtc::ExplicitZeroMemory(&key_str[0], key_str.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
148
pc/srtp_filter.h
Normal file
148
pc/srtp_filter.h
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef PC_SRTP_FILTER_H_
|
||||
#define PC_SRTP_FILTER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/crypto_params.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
#include "rtc_base/ssl_stream_adapter.h"
|
||||
|
||||
// Forward declaration to avoid pulling in libsrtp headers here
|
||||
struct srtp_event_data_t;
|
||||
struct srtp_ctx_t_;
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// A helper class used to negotiate SDES crypto params.
|
||||
// TODO(zhihuang): Find a better name for this class, like "SdesNegotiator".
|
||||
class SrtpFilter {
|
||||
public:
|
||||
enum Mode { PROTECT, UNPROTECT };
|
||||
enum Error {
|
||||
ERROR_NONE,
|
||||
ERROR_FAIL,
|
||||
ERROR_AUTH,
|
||||
ERROR_REPLAY,
|
||||
};
|
||||
|
||||
SrtpFilter();
|
||||
~SrtpFilter();
|
||||
|
||||
// Whether the filter is active (i.e. crypto has been properly negotiated).
|
||||
bool IsActive() const;
|
||||
|
||||
// Handle the offer/answer negotiation of the crypto parameters internally.
|
||||
// TODO(zhihuang): Make SetOffer/ProvisionalAnswer/Answer private as helper
|
||||
// methods once start using Process.
|
||||
bool Process(const std::vector<CryptoParams>& cryptos,
|
||||
webrtc::SdpType type,
|
||||
ContentSource source);
|
||||
|
||||
// Indicates which crypto algorithms and keys were contained in the offer.
|
||||
// offer_params should contain a list of available parameters to use, or none,
|
||||
// if crypto is not desired. This must be called before SetAnswer.
|
||||
bool SetOffer(const std::vector<CryptoParams>& offer_params,
|
||||
ContentSource source);
|
||||
// Same as SetAnwer. But multiple calls are allowed to SetProvisionalAnswer
|
||||
// after a call to SetOffer.
|
||||
bool SetProvisionalAnswer(const std::vector<CryptoParams>& answer_params,
|
||||
ContentSource source);
|
||||
// Indicates which crypto algorithms and keys were contained in the answer.
|
||||
// answer_params should contain the negotiated parameters, which may be none,
|
||||
// if crypto was not desired or could not be negotiated (and not required).
|
||||
// This must be called after SetOffer. If crypto negotiation completes
|
||||
// successfully, this will advance the filter to the active state.
|
||||
bool SetAnswer(const std::vector<CryptoParams>& answer_params,
|
||||
ContentSource source);
|
||||
|
||||
bool ResetParams();
|
||||
|
||||
static bool ParseKeyParams(const std::string& params,
|
||||
uint8_t* key,
|
||||
size_t len);
|
||||
|
||||
absl::optional<int> send_cipher_suite() { return send_cipher_suite_; }
|
||||
absl::optional<int> recv_cipher_suite() { return recv_cipher_suite_; }
|
||||
|
||||
rtc::ArrayView<const uint8_t> send_key() { return send_key_; }
|
||||
rtc::ArrayView<const uint8_t> recv_key() { return recv_key_; }
|
||||
|
||||
protected:
|
||||
bool ExpectOffer(ContentSource source);
|
||||
|
||||
bool StoreParams(const std::vector<CryptoParams>& params,
|
||||
ContentSource source);
|
||||
|
||||
bool ExpectAnswer(ContentSource source);
|
||||
|
||||
bool DoSetAnswer(const std::vector<CryptoParams>& answer_params,
|
||||
ContentSource source,
|
||||
bool final);
|
||||
|
||||
bool NegotiateParams(const std::vector<CryptoParams>& answer_params,
|
||||
CryptoParams* selected_params);
|
||||
|
||||
private:
|
||||
bool ApplySendParams(const CryptoParams& send_params);
|
||||
|
||||
bool ApplyRecvParams(const CryptoParams& recv_params);
|
||||
|
||||
enum State {
|
||||
ST_INIT, // SRTP filter unused.
|
||||
ST_SENTOFFER, // Offer with SRTP parameters sent.
|
||||
ST_RECEIVEDOFFER, // Offer with SRTP parameters received.
|
||||
ST_SENTPRANSWER_NO_CRYPTO, // Sent provisional answer without crypto.
|
||||
// Received provisional answer without crypto.
|
||||
ST_RECEIVEDPRANSWER_NO_CRYPTO,
|
||||
ST_ACTIVE, // Offer and answer set.
|
||||
// SRTP filter is active but new parameters are offered.
|
||||
// When the answer is set, the state transitions to ST_ACTIVE or ST_INIT.
|
||||
ST_SENTUPDATEDOFFER,
|
||||
// SRTP filter is active but new parameters are received.
|
||||
// When the answer is set, the state transitions back to ST_ACTIVE.
|
||||
ST_RECEIVEDUPDATEDOFFER,
|
||||
// SRTP filter is active but the sent answer is only provisional.
|
||||
// When the final answer is set, the state transitions to ST_ACTIVE or
|
||||
// ST_INIT.
|
||||
ST_SENTPRANSWER,
|
||||
// SRTP filter is active but the received answer is only provisional.
|
||||
// When the final answer is set, the state transitions to ST_ACTIVE or
|
||||
// ST_INIT.
|
||||
ST_RECEIVEDPRANSWER
|
||||
};
|
||||
State state_ = ST_INIT;
|
||||
std::vector<CryptoParams> offer_params_;
|
||||
CryptoParams applied_send_params_;
|
||||
CryptoParams applied_recv_params_;
|
||||
absl::optional<int> send_cipher_suite_;
|
||||
absl::optional<int> recv_cipher_suite_;
|
||||
rtc::ZeroOnFreeBuffer<uint8_t> send_key_;
|
||||
rtc::ZeroOnFreeBuffer<uint8_t> recv_key_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // PC_SRTP_FILTER_H_
|
||||
472
pc/srtp_filter_unittest.cc
Normal file
472
pc/srtp_filter_unittest.cc
Normal file
@ -0,0 +1,472 @@
|
||||
/*
|
||||
* Copyright 2004 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/srtp_filter.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "api/crypto_params.h"
|
||||
#include "rtc_base/ssl_stream_adapter.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
using cricket::CryptoParams;
|
||||
using cricket::CS_LOCAL;
|
||||
using cricket::CS_REMOTE;
|
||||
|
||||
namespace rtc {
|
||||
|
||||
static const char kTestKeyParams1[] =
|
||||
"inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz";
|
||||
static const char kTestKeyParams2[] =
|
||||
"inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR";
|
||||
static const char kTestKeyParams3[] =
|
||||
"inline:1234X19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz";
|
||||
static const char kTestKeyParams4[] =
|
||||
"inline:4567QCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR";
|
||||
static const char kTestKeyParamsGcm1[] =
|
||||
"inline:e166KFlKzJsGW0d5apX+rrI05vxbrvMJEzFI14aTDCa63IRTlLK4iH66uOI=";
|
||||
static const char kTestKeyParamsGcm2[] =
|
||||
"inline:6X0oCd55zfz4VgtOwsuqcFq61275PDYN5uwuu3p7ZUHbfUY2FMpdP4m2PEo=";
|
||||
static const char kTestKeyParamsGcm3[] =
|
||||
"inline:YKlABGZWMgX32xuMotrG0v0T7G83veegaVzubQ==";
|
||||
static const char kTestKeyParamsGcm4[] =
|
||||
"inline:gJ6tWoUym2v+/F6xjr7xaxiS3QbJJozl3ZD/0A==";
|
||||
static const cricket::CryptoParams kTestCryptoParams1(1,
|
||||
"AES_CM_128_HMAC_SHA1_80",
|
||||
kTestKeyParams1,
|
||||
"");
|
||||
static const cricket::CryptoParams kTestCryptoParams2(1,
|
||||
"AES_CM_128_HMAC_SHA1_80",
|
||||
kTestKeyParams2,
|
||||
"");
|
||||
static const cricket::CryptoParams kTestCryptoParamsGcm1(1,
|
||||
"AEAD_AES_256_GCM",
|
||||
kTestKeyParamsGcm1,
|
||||
"");
|
||||
static const cricket::CryptoParams kTestCryptoParamsGcm2(1,
|
||||
"AEAD_AES_256_GCM",
|
||||
kTestKeyParamsGcm2,
|
||||
"");
|
||||
static const cricket::CryptoParams kTestCryptoParamsGcm3(1,
|
||||
"AEAD_AES_128_GCM",
|
||||
kTestKeyParamsGcm3,
|
||||
"");
|
||||
static const cricket::CryptoParams kTestCryptoParamsGcm4(1,
|
||||
"AEAD_AES_128_GCM",
|
||||
kTestKeyParamsGcm4,
|
||||
"");
|
||||
|
||||
class SrtpFilterTest : public ::testing::Test {
|
||||
protected:
|
||||
SrtpFilterTest() {}
|
||||
static std::vector<CryptoParams> MakeVector(const CryptoParams& params) {
|
||||
std::vector<CryptoParams> vec;
|
||||
vec.push_back(params);
|
||||
return vec;
|
||||
}
|
||||
|
||||
void TestSetParams(const std::vector<CryptoParams>& params1,
|
||||
const std::vector<CryptoParams>& params2) {
|
||||
EXPECT_TRUE(f1_.SetOffer(params1, CS_LOCAL));
|
||||
EXPECT_TRUE(f2_.SetOffer(params1, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_FALSE(f2_.IsActive());
|
||||
EXPECT_TRUE(f2_.SetAnswer(params2, CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetAnswer(params2, CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
EXPECT_TRUE(f2_.IsActive());
|
||||
}
|
||||
|
||||
void VerifyKeysAreEqual(ArrayView<const uint8_t> key1,
|
||||
ArrayView<const uint8_t> key2) {
|
||||
EXPECT_EQ(key1.size(), key2.size());
|
||||
EXPECT_EQ(0, memcmp(key1.data(), key2.data(), key1.size()));
|
||||
}
|
||||
|
||||
void VerifyCryptoParamsMatch(const std::string& cs1, const std::string& cs2) {
|
||||
EXPECT_EQ(rtc::SrtpCryptoSuiteFromName(cs1), f1_.send_cipher_suite());
|
||||
EXPECT_EQ(rtc::SrtpCryptoSuiteFromName(cs2), f2_.send_cipher_suite());
|
||||
VerifyKeysAreEqual(f1_.send_key(), f2_.recv_key());
|
||||
VerifyKeysAreEqual(f2_.send_key(), f1_.recv_key());
|
||||
}
|
||||
|
||||
cricket::SrtpFilter f1_;
|
||||
cricket::SrtpFilter f2_;
|
||||
};
|
||||
|
||||
// Test that we can set up the session and keys properly.
|
||||
TEST_F(SrtpFilterTest, TestGoodSetupOneCipherSuite) {
|
||||
EXPECT_TRUE(f1_.SetOffer(MakeVector(kTestCryptoParams1), CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_TRUE(f1_.SetAnswer(MakeVector(kTestCryptoParams2), CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
}
|
||||
|
||||
TEST_F(SrtpFilterTest, TestGoodSetupOneCipherSuiteGcm) {
|
||||
EXPECT_TRUE(f1_.SetOffer(MakeVector(kTestCryptoParamsGcm1), CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_TRUE(f1_.SetAnswer(MakeVector(kTestCryptoParamsGcm2), CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we can set up things with multiple params.
|
||||
TEST_F(SrtpFilterTest, TestGoodSetupMultipleCipherSuites) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
offer.push_back(kTestCryptoParams1);
|
||||
offer[1].tag = 2;
|
||||
offer[1].cipher_suite = kCsAesCm128HmacSha1_32;
|
||||
answer[0].tag = 2;
|
||||
answer[0].cipher_suite = kCsAesCm128HmacSha1_32;
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
}
|
||||
|
||||
TEST_F(SrtpFilterTest, TestGoodSetupMultipleCipherSuitesGcm) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParamsGcm1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParamsGcm3));
|
||||
offer.push_back(kTestCryptoParamsGcm4);
|
||||
offer[1].tag = 2;
|
||||
answer[0].tag = 2;
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we handle the cases where crypto is not desired.
|
||||
TEST_F(SrtpFilterTest, TestGoodSetupNoCipherSuites) {
|
||||
std::vector<CryptoParams> offer, answer;
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we handle the cases where crypto is not desired by the remote side.
|
||||
TEST_F(SrtpFilterTest, TestGoodSetupNoAnswerCipherSuites) {
|
||||
std::vector<CryptoParams> answer;
|
||||
EXPECT_TRUE(f1_.SetOffer(MakeVector(kTestCryptoParams1), CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we fail if we call the functions the wrong way.
|
||||
TEST_F(SrtpFilterTest, TestBadSetup) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
EXPECT_FALSE(f1_.SetAnswer(answer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetAnswer(answer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we can set offer multiple times from the same source.
|
||||
TEST_F(SrtpFilterTest, TestGoodSetupMultipleOffers) {
|
||||
EXPECT_TRUE(f1_.SetOffer(MakeVector(kTestCryptoParams1), CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetOffer(MakeVector(kTestCryptoParams2), CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_TRUE(f1_.SetAnswer(MakeVector(kTestCryptoParams2), CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
EXPECT_TRUE(f1_.SetOffer(MakeVector(kTestCryptoParams1), CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetOffer(MakeVector(kTestCryptoParams2), CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetAnswer(MakeVector(kTestCryptoParams2), CS_REMOTE));
|
||||
|
||||
EXPECT_TRUE(f2_.SetOffer(MakeVector(kTestCryptoParams1), CS_REMOTE));
|
||||
EXPECT_TRUE(f2_.SetOffer(MakeVector(kTestCryptoParams2), CS_REMOTE));
|
||||
EXPECT_FALSE(f2_.IsActive());
|
||||
EXPECT_TRUE(f2_.SetAnswer(MakeVector(kTestCryptoParams2), CS_LOCAL));
|
||||
EXPECT_TRUE(f2_.IsActive());
|
||||
EXPECT_TRUE(f2_.SetOffer(MakeVector(kTestCryptoParams1), CS_REMOTE));
|
||||
EXPECT_TRUE(f2_.SetOffer(MakeVector(kTestCryptoParams2), CS_REMOTE));
|
||||
EXPECT_TRUE(f2_.SetAnswer(MakeVector(kTestCryptoParams2), CS_LOCAL));
|
||||
}
|
||||
// Test that we can't set offer multiple times from different sources.
|
||||
TEST_F(SrtpFilterTest, TestBadSetupMultipleOffers) {
|
||||
EXPECT_TRUE(f1_.SetOffer(MakeVector(kTestCryptoParams1), CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetOffer(MakeVector(kTestCryptoParams2), CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_TRUE(f1_.SetAnswer(MakeVector(kTestCryptoParams1), CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
EXPECT_TRUE(f1_.SetOffer(MakeVector(kTestCryptoParams2), CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetOffer(MakeVector(kTestCryptoParams1), CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.SetAnswer(MakeVector(kTestCryptoParams2), CS_REMOTE));
|
||||
|
||||
EXPECT_TRUE(f2_.SetOffer(MakeVector(kTestCryptoParams2), CS_REMOTE));
|
||||
EXPECT_FALSE(f2_.SetOffer(MakeVector(kTestCryptoParams1), CS_LOCAL));
|
||||
EXPECT_FALSE(f2_.IsActive());
|
||||
EXPECT_TRUE(f2_.SetAnswer(MakeVector(kTestCryptoParams2), CS_LOCAL));
|
||||
EXPECT_TRUE(f2_.IsActive());
|
||||
EXPECT_TRUE(f2_.SetOffer(MakeVector(kTestCryptoParams2), CS_REMOTE));
|
||||
EXPECT_FALSE(f2_.SetOffer(MakeVector(kTestCryptoParams1), CS_LOCAL));
|
||||
EXPECT_TRUE(f2_.SetAnswer(MakeVector(kTestCryptoParams2), CS_LOCAL));
|
||||
}
|
||||
|
||||
// Test that we fail if we have params in the answer when none were offered.
|
||||
TEST_F(SrtpFilterTest, TestNoAnswerCipherSuites) {
|
||||
std::vector<CryptoParams> offer;
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetAnswer(MakeVector(kTestCryptoParams2), CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we fail if we have too many params in our answer.
|
||||
TEST_F(SrtpFilterTest, TestMultipleAnswerCipherSuites) {
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
answer.push_back(kTestCryptoParams2);
|
||||
answer[1].tag = 2;
|
||||
answer[1].cipher_suite = kCsAesCm128HmacSha1_32;
|
||||
EXPECT_TRUE(f1_.SetOffer(MakeVector(kTestCryptoParams1), CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we fail if we don't support the cipher-suite.
|
||||
TEST_F(SrtpFilterTest, TestInvalidCipherSuite) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
offer[0].cipher_suite = answer[0].cipher_suite = "FOO";
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we fail if we can't agree on a tag.
|
||||
TEST_F(SrtpFilterTest, TestNoMatchingTag) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
answer[0].tag = 99;
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we fail if we can't agree on a cipher-suite.
|
||||
TEST_F(SrtpFilterTest, TestNoMatchingCipherSuite) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
answer[0].tag = 2;
|
||||
answer[0].cipher_suite = "FOO";
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we fail keys with bad base64 content.
|
||||
TEST_F(SrtpFilterTest, TestInvalidKeyData) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
answer[0].key_params = "inline:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we fail keys with the wrong key-method.
|
||||
TEST_F(SrtpFilterTest, TestWrongKeyMethod) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
answer[0].key_params = "outline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR";
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we fail keys of the wrong length.
|
||||
TEST_F(SrtpFilterTest, TestKeyTooShort) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
answer[0].key_params = "inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtx";
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we fail keys of the wrong length.
|
||||
TEST_F(SrtpFilterTest, TestKeyTooLong) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
answer[0].key_params = "inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBRABCD";
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we fail keys with lifetime or MKI set (since we don't support)
|
||||
TEST_F(SrtpFilterTest, TestUnsupportedOptions) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
answer[0].key_params =
|
||||
"inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:4";
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
}
|
||||
|
||||
// Test that we can encrypt/decrypt after negotiating AES_CM_128_HMAC_SHA1_80.
|
||||
TEST_F(SrtpFilterTest, TestProtect_AES_CM_128_HMAC_SHA1_80) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
offer.push_back(kTestCryptoParams1);
|
||||
offer[1].tag = 2;
|
||||
offer[1].cipher_suite = kCsAesCm128HmacSha1_32;
|
||||
TestSetParams(offer, answer);
|
||||
VerifyCryptoParamsMatch(kCsAesCm128HmacSha1_80, kCsAesCm128HmacSha1_80);
|
||||
}
|
||||
|
||||
// Test that we can encrypt/decrypt after negotiating AES_CM_128_HMAC_SHA1_32.
|
||||
TEST_F(SrtpFilterTest, TestProtect_AES_CM_128_HMAC_SHA1_32) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
offer.push_back(kTestCryptoParams1);
|
||||
offer[1].tag = 2;
|
||||
offer[1].cipher_suite = kCsAesCm128HmacSha1_32;
|
||||
answer[0].tag = 2;
|
||||
answer[0].cipher_suite = kCsAesCm128HmacSha1_32;
|
||||
TestSetParams(offer, answer);
|
||||
VerifyCryptoParamsMatch(kCsAesCm128HmacSha1_32, kCsAesCm128HmacSha1_32);
|
||||
}
|
||||
|
||||
// Test that we can change encryption parameters.
|
||||
TEST_F(SrtpFilterTest, TestChangeParameters) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
|
||||
TestSetParams(offer, answer);
|
||||
VerifyCryptoParamsMatch(kCsAesCm128HmacSha1_80, kCsAesCm128HmacSha1_80);
|
||||
|
||||
// Change the key parameters and cipher_suite.
|
||||
offer[0].key_params = kTestKeyParams3;
|
||||
offer[0].cipher_suite = kCsAesCm128HmacSha1_32;
|
||||
answer[0].key_params = kTestKeyParams4;
|
||||
answer[0].cipher_suite = kCsAesCm128HmacSha1_32;
|
||||
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_TRUE(f2_.SetOffer(offer, CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
|
||||
// Test that the old keys are valid until the negotiation is complete.
|
||||
VerifyCryptoParamsMatch(kCsAesCm128HmacSha1_80, kCsAesCm128HmacSha1_80);
|
||||
|
||||
// Complete the negotiation and test that we can still understand each other.
|
||||
EXPECT_TRUE(f2_.SetAnswer(answer, CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
|
||||
VerifyCryptoParamsMatch(kCsAesCm128HmacSha1_32, kCsAesCm128HmacSha1_32);
|
||||
}
|
||||
|
||||
// Test that we can send and receive provisional answers with crypto enabled.
|
||||
// Also test that we can change the crypto.
|
||||
TEST_F(SrtpFilterTest, TestProvisionalAnswer) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
offer.push_back(kTestCryptoParams1);
|
||||
offer[1].tag = 2;
|
||||
offer[1].cipher_suite = kCsAesCm128HmacSha1_32;
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_TRUE(f2_.SetOffer(offer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_FALSE(f2_.IsActive());
|
||||
EXPECT_TRUE(f2_.SetProvisionalAnswer(answer, CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetProvisionalAnswer(answer, CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
EXPECT_TRUE(f2_.IsActive());
|
||||
VerifyCryptoParamsMatch(kCsAesCm128HmacSha1_80, kCsAesCm128HmacSha1_80);
|
||||
|
||||
answer[0].key_params = kTestKeyParams4;
|
||||
answer[0].tag = 2;
|
||||
answer[0].cipher_suite = kCsAesCm128HmacSha1_32;
|
||||
EXPECT_TRUE(f2_.SetAnswer(answer, CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
EXPECT_TRUE(f2_.IsActive());
|
||||
VerifyCryptoParamsMatch(kCsAesCm128HmacSha1_32, kCsAesCm128HmacSha1_32);
|
||||
}
|
||||
|
||||
// Test that a provisional answer doesn't need to contain a crypto.
|
||||
TEST_F(SrtpFilterTest, TestProvisionalAnswerWithoutCrypto) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer;
|
||||
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_TRUE(f2_.SetOffer(offer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_FALSE(f2_.IsActive());
|
||||
EXPECT_TRUE(f2_.SetProvisionalAnswer(answer, CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetProvisionalAnswer(answer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_FALSE(f2_.IsActive());
|
||||
|
||||
answer.push_back(kTestCryptoParams2);
|
||||
EXPECT_TRUE(f2_.SetAnswer(answer, CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
EXPECT_TRUE(f2_.IsActive());
|
||||
VerifyCryptoParamsMatch(kCsAesCm128HmacSha1_80, kCsAesCm128HmacSha1_80);
|
||||
}
|
||||
|
||||
// Test that if we get a new local offer after a provisional answer
|
||||
// with no crypto, that we are in an inactive state.
|
||||
TEST_F(SrtpFilterTest, TestLocalOfferAfterProvisionalAnswerWithoutCrypto) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer;
|
||||
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_TRUE(f2_.SetOffer(offer, CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.SetProvisionalAnswer(answer, CS_REMOTE));
|
||||
EXPECT_TRUE(f2_.SetProvisionalAnswer(answer, CS_LOCAL));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_FALSE(f2_.IsActive());
|
||||
// The calls to set an offer after a provisional answer fail, so the
|
||||
// state doesn't change.
|
||||
EXPECT_FALSE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_FALSE(f2_.SetOffer(offer, CS_REMOTE));
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_FALSE(f2_.IsActive());
|
||||
|
||||
answer.push_back(kTestCryptoParams2);
|
||||
EXPECT_TRUE(f2_.SetAnswer(answer, CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
EXPECT_TRUE(f2_.IsActive());
|
||||
VerifyCryptoParamsMatch(kCsAesCm128HmacSha1_80, kCsAesCm128HmacSha1_80);
|
||||
}
|
||||
|
||||
// Test that we can disable encryption.
|
||||
TEST_F(SrtpFilterTest, TestDisableEncryption) {
|
||||
std::vector<CryptoParams> offer(MakeVector(kTestCryptoParams1));
|
||||
std::vector<CryptoParams> answer(MakeVector(kTestCryptoParams2));
|
||||
|
||||
TestSetParams(offer, answer);
|
||||
VerifyCryptoParamsMatch(kCsAesCm128HmacSha1_80, kCsAesCm128HmacSha1_80);
|
||||
|
||||
offer.clear();
|
||||
answer.clear();
|
||||
EXPECT_TRUE(f1_.SetOffer(offer, CS_LOCAL));
|
||||
EXPECT_TRUE(f2_.SetOffer(offer, CS_REMOTE));
|
||||
EXPECT_TRUE(f1_.IsActive());
|
||||
EXPECT_TRUE(f2_.IsActive());
|
||||
|
||||
// Test that the old keys are valid until the negotiation is complete.
|
||||
VerifyCryptoParamsMatch(kCsAesCm128HmacSha1_80, kCsAesCm128HmacSha1_80);
|
||||
|
||||
// Complete the negotiation.
|
||||
EXPECT_TRUE(f2_.SetAnswer(answer, CS_LOCAL));
|
||||
EXPECT_TRUE(f1_.SetAnswer(answer, CS_REMOTE));
|
||||
|
||||
EXPECT_FALSE(f1_.IsActive());
|
||||
EXPECT_FALSE(f2_.IsActive());
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
@ -37,6 +37,86 @@ namespace webrtc {
|
||||
SrtpTransport::SrtpTransport(bool rtcp_mux_enabled)
|
||||
: RtpTransport(rtcp_mux_enabled) {}
|
||||
|
||||
RTCError SrtpTransport::SetSrtpSendKey(const cricket::CryptoParams& params) {
|
||||
if (send_params_) {
|
||||
LOG_AND_RETURN_ERROR(
|
||||
webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
|
||||
"Setting the SRTP send key twice is currently unsupported.");
|
||||
}
|
||||
if (recv_params_ && recv_params_->cipher_suite != params.cipher_suite) {
|
||||
LOG_AND_RETURN_ERROR(
|
||||
webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
|
||||
"The send key and receive key must have the same cipher suite.");
|
||||
}
|
||||
|
||||
send_cipher_suite_ = rtc::SrtpCryptoSuiteFromName(params.cipher_suite);
|
||||
if (*send_cipher_suite_ == rtc::kSrtpInvalidCryptoSuite) {
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
"Invalid SRTP crypto suite");
|
||||
}
|
||||
|
||||
int send_key_len, send_salt_len;
|
||||
if (!rtc::GetSrtpKeyAndSaltLengths(*send_cipher_suite_, &send_key_len,
|
||||
&send_salt_len)) {
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
"Could not get lengths for crypto suite(s):"
|
||||
" send cipher_suite ");
|
||||
}
|
||||
|
||||
send_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(send_key_len + send_salt_len);
|
||||
if (!ParseKeyParams(params.key_params, send_key_.data(), send_key_.size())) {
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to parse the crypto key params");
|
||||
}
|
||||
|
||||
if (!MaybeSetKeyParams()) {
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to set the crypto key params");
|
||||
}
|
||||
send_params_ = params;
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
RTCError SrtpTransport::SetSrtpReceiveKey(const cricket::CryptoParams& params) {
|
||||
if (recv_params_) {
|
||||
LOG_AND_RETURN_ERROR(
|
||||
webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
|
||||
"Setting the SRTP send key twice is currently unsupported.");
|
||||
}
|
||||
if (send_params_ && send_params_->cipher_suite != params.cipher_suite) {
|
||||
LOG_AND_RETURN_ERROR(
|
||||
webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
|
||||
"The send key and receive key must have the same cipher suite.");
|
||||
}
|
||||
|
||||
recv_cipher_suite_ = rtc::SrtpCryptoSuiteFromName(params.cipher_suite);
|
||||
if (*recv_cipher_suite_ == rtc::kSrtpInvalidCryptoSuite) {
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
"Invalid SRTP crypto suite");
|
||||
}
|
||||
|
||||
int recv_key_len, recv_salt_len;
|
||||
if (!rtc::GetSrtpKeyAndSaltLengths(*recv_cipher_suite_, &recv_key_len,
|
||||
&recv_salt_len)) {
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
"Could not get lengths for crypto suite(s):"
|
||||
" recv cipher_suite ");
|
||||
}
|
||||
|
||||
recv_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(recv_key_len + recv_salt_len);
|
||||
if (!ParseKeyParams(params.key_params, recv_key_.data(), recv_key_.size())) {
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to parse the crypto key params");
|
||||
}
|
||||
|
||||
if (!MaybeSetKeyParams()) {
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to set the crypto key params");
|
||||
}
|
||||
recv_params_ = params;
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
bool SrtpTransport::SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags) {
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/crypto_params.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "p2p/base/packet_transport_internal.h"
|
||||
#include "pc/rtp_transport.h"
|
||||
@ -39,6 +40,10 @@ class SrtpTransport : public RtpTransport {
|
||||
|
||||
virtual ~SrtpTransport() = default;
|
||||
|
||||
// SrtpTransportInterface specific implementation.
|
||||
virtual RTCError SetSrtpSendKey(const cricket::CryptoParams& params);
|
||||
virtual RTCError SetSrtpReceiveKey(const cricket::CryptoParams& params);
|
||||
|
||||
bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags) override;
|
||||
@ -148,6 +153,8 @@ class SrtpTransport : public RtpTransport {
|
||||
std::unique_ptr<cricket::SrtpSession> send_rtcp_session_;
|
||||
std::unique_ptr<cricket::SrtpSession> recv_rtcp_session_;
|
||||
|
||||
absl::optional<cricket::CryptoParams> send_params_;
|
||||
absl::optional<cricket::CryptoParams> recv_params_;
|
||||
absl::optional<int> send_cipher_suite_;
|
||||
absl::optional<int> recv_cipher_suite_;
|
||||
rtc::ZeroOnFreeBuffer<uint8_t> send_key_;
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/crypto_params.h"
|
||||
#include "api/jsep_ice_candidate.h"
|
||||
#include "api/jsep_session_description.h"
|
||||
#include "api/media_types.h"
|
||||
@ -69,6 +70,7 @@ using cricket::AudioContentDescription;
|
||||
using cricket::Candidate;
|
||||
using cricket::Candidates;
|
||||
using cricket::ContentInfo;
|
||||
using cricket::CryptoParams;
|
||||
using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
|
||||
using cricket::ICE_CANDIDATE_COMPONENT_RTP;
|
||||
using cricket::kApplicationSpecificBandwidth;
|
||||
@ -152,6 +154,7 @@ static const char kNoStreamMsid[] = "-";
|
||||
static const char kSsrcAttributeMslabel[] = "mslabel";
|
||||
static const char kSSrcAttributeLabel[] = "label";
|
||||
static const char kAttributeSsrcGroup[] = "ssrc-group";
|
||||
static const char kAttributeCrypto[] = "crypto";
|
||||
static const char kAttributeCandidate[] = "candidate";
|
||||
static const char kAttributeCandidateTyp[] = "typ";
|
||||
static const char kAttributeCandidateRaddr[] = "raddr";
|
||||
@ -329,6 +332,9 @@ static bool ParseSsrcAttribute(const std::string& line,
|
||||
static bool ParseSsrcGroupAttribute(const std::string& line,
|
||||
SsrcGroupVec* ssrc_groups,
|
||||
SdpParseError* error);
|
||||
static bool ParseCryptoAttribute(const std::string& line,
|
||||
MediaContentDescription* media_desc,
|
||||
SdpParseError* error);
|
||||
static bool ParseRtpmapAttribute(const std::string& line,
|
||||
const cricket::MediaType media_type,
|
||||
const std::vector<int>& payload_types,
|
||||
@ -1671,6 +1677,18 @@ void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
|
||||
AddLine(os.str(), message);
|
||||
}
|
||||
|
||||
// RFC 4568
|
||||
// a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
|
||||
for (const CryptoParams& crypto_params : media_desc->cryptos()) {
|
||||
InitAttrLine(kAttributeCrypto, &os);
|
||||
os << kSdpDelimiterColon << crypto_params.tag << " "
|
||||
<< crypto_params.cipher_suite << " " << crypto_params.key_params;
|
||||
if (!crypto_params.session_params.empty()) {
|
||||
os << " " << crypto_params.session_params;
|
||||
}
|
||||
AddLine(os.str(), message);
|
||||
}
|
||||
|
||||
// RFC 4566
|
||||
// a=rtpmap:<payload type> <encoding name>/<clock rate>
|
||||
// [/<encodingparameters>]
|
||||
@ -3152,6 +3170,10 @@ bool ParseContent(const std::string& message,
|
||||
if (!ParseSsrcAttribute(line, &ssrc_infos, msid_signaling, error)) {
|
||||
return false;
|
||||
}
|
||||
} else if (HasAttribute(line, kAttributeCrypto)) {
|
||||
if (!ParseCryptoAttribute(line, media_desc, error)) {
|
||||
return false;
|
||||
}
|
||||
} else if (HasAttribute(line, kAttributeRtpmap)) {
|
||||
if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
|
||||
error)) {
|
||||
@ -3481,6 +3503,36 @@ bool ParseSsrcGroupAttribute(const std::string& line,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseCryptoAttribute(const std::string& line,
|
||||
MediaContentDescription* media_desc,
|
||||
SdpParseError* error) {
|
||||
std::vector<std::string> fields;
|
||||
rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
|
||||
// RFC 4568
|
||||
// a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
|
||||
const size_t expected_min_fields = 3;
|
||||
if (fields.size() < expected_min_fields) {
|
||||
return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
|
||||
}
|
||||
std::string tag_value;
|
||||
if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
|
||||
return false;
|
||||
}
|
||||
int tag = 0;
|
||||
if (!GetValueFromString(line, tag_value, &tag, error)) {
|
||||
return false;
|
||||
}
|
||||
const std::string& crypto_suite = fields[1];
|
||||
const std::string& key_params = fields[2];
|
||||
std::string session_params;
|
||||
if (fields.size() > 3) {
|
||||
session_params = fields[3];
|
||||
}
|
||||
media_desc->AddCrypto(
|
||||
CryptoParams(tag, crypto_suite, key_params, session_params));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Updates or creates a new codec entry in the audio description with according
|
||||
// to `name`, `clockrate`, `bitrate`, and `channels`.
|
||||
void UpdateCodec(int payload_type,
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/crypto_params.h"
|
||||
#include "api/jsep_session_description.h"
|
||||
#include "api/media_types.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
@ -54,6 +55,7 @@ using cricket::AudioContentDescription;
|
||||
using cricket::Candidate;
|
||||
using cricket::ContentGroup;
|
||||
using cricket::ContentInfo;
|
||||
using cricket::CryptoParams;
|
||||
using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
|
||||
using cricket::ICE_CANDIDATE_COMPONENT_RTP;
|
||||
using cricket::kFecSsrcGroupSemantics;
|
||||
@ -139,9 +141,9 @@ struct CodecParams {
|
||||
int maxaveragebitrate;
|
||||
};
|
||||
|
||||
// Note: The reference strings do not contain a=fingerprint, which is
|
||||
// required for DTLS negotiation.
|
||||
// Neither do they contain the obsolete a=crypto lines.
|
||||
// TODO(deadbeef): In these reference strings, use "a=fingerprint" by default
|
||||
// instead of "a=crypto", and have an explicit test for adding "a=crypto".
|
||||
// Currently it's the other way around.
|
||||
|
||||
// Reference sdp string
|
||||
static const char kSdpFullString[] =
|
||||
@ -173,6 +175,9 @@ static const char kSdpFullString[] =
|
||||
"a=sendrecv\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=rtcp-rsize\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
|
||||
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
|
||||
"dummy_session_params\r\n"
|
||||
"a=rtpmap:111 opus/48000/2\r\n"
|
||||
"a=rtpmap:103 ISAC/16000\r\n"
|
||||
"a=rtpmap:104 ISAC/32000\r\n"
|
||||
@ -198,6 +203,8 @@ static const char kSdpFullString[] =
|
||||
"a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
|
||||
"a=mid:video_content_name\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
|
||||
"inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
|
||||
"a=rtpmap:120 VP8/90000\r\n"
|
||||
"a=ssrc-group:FEC 2 3\r\n"
|
||||
"a=ssrc:2 cname:stream_1_cname\r\n"
|
||||
@ -225,6 +232,9 @@ static const char kSdpString[] =
|
||||
"a=sendrecv\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=rtcp-rsize\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
|
||||
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
|
||||
"dummy_session_params\r\n"
|
||||
"a=rtpmap:111 opus/48000/2\r\n"
|
||||
"a=rtpmap:103 ISAC/16000\r\n"
|
||||
"a=rtpmap:104 ISAC/32000\r\n"
|
||||
@ -238,6 +248,8 @@ static const char kSdpString[] =
|
||||
"a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
|
||||
"a=mid:video_content_name\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
|
||||
"inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
|
||||
"a=rtpmap:120 VP8/90000\r\n"
|
||||
"a=ssrc-group:FEC 2 3\r\n"
|
||||
"a=ssrc:2 cname:stream_1_cname\r\n"
|
||||
@ -370,6 +382,9 @@ static const char kBundleOnlySdpFullString[] =
|
||||
"a=sendrecv\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=rtcp-rsize\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
|
||||
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
|
||||
"dummy_session_params\r\n"
|
||||
"a=rtpmap:111 opus/48000/2\r\n"
|
||||
"a=rtpmap:103 ISAC/16000\r\n"
|
||||
"a=rtpmap:104 ISAC/32000\r\n"
|
||||
@ -383,6 +398,8 @@ static const char kBundleOnlySdpFullString[] =
|
||||
"a=bundle-only\r\n"
|
||||
"a=mid:video_content_name\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
|
||||
"inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
|
||||
"a=rtpmap:120 VP8/90000\r\n"
|
||||
"a=ssrc-group:FEC 2 3\r\n"
|
||||
"a=ssrc:2 cname:stream_1_cname\r\n"
|
||||
@ -425,6 +442,9 @@ static const char kPlanBSdpFullString[] =
|
||||
"a=sendrecv\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=rtcp-rsize\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
|
||||
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
|
||||
"dummy_session_params\r\n"
|
||||
"a=rtpmap:111 opus/48000/2\r\n"
|
||||
"a=rtpmap:103 ISAC/16000\r\n"
|
||||
"a=rtpmap:104 ISAC/32000\r\n"
|
||||
@ -454,6 +474,8 @@ static const char kPlanBSdpFullString[] =
|
||||
"a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
|
||||
"a=mid:video_content_name\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
|
||||
"inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
|
||||
"a=rtpmap:120 VP8/90000\r\n"
|
||||
"a=ssrc-group:FEC 2 3\r\n"
|
||||
"a=ssrc:2 cname:stream_1_cname\r\n"
|
||||
@ -506,6 +528,9 @@ static const char kUnifiedPlanSdpFullString[] =
|
||||
"a=sendrecv\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=rtcp-rsize\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
|
||||
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
|
||||
"dummy_session_params\r\n"
|
||||
"a=rtpmap:111 opus/48000/2\r\n"
|
||||
"a=rtpmap:103 ISAC/16000\r\n"
|
||||
"a=rtpmap:104 ISAC/32000\r\n"
|
||||
@ -530,6 +555,8 @@ static const char kUnifiedPlanSdpFullString[] =
|
||||
"a=mid:video_content_name\r\n"
|
||||
"a=msid:local_stream_1 video_track_id_1\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
|
||||
"inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
|
||||
"a=rtpmap:120 VP8/90000\r\n"
|
||||
"a=ssrc-group:FEC 2 3\r\n"
|
||||
"a=ssrc:2 cname:stream_1_cname\r\n"
|
||||
@ -544,6 +571,9 @@ static const char kUnifiedPlanSdpFullString[] =
|
||||
"a=sendrecv\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=rtcp-rsize\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
|
||||
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
|
||||
"dummy_session_params\r\n"
|
||||
"a=rtpmap:111 opus/48000/2\r\n"
|
||||
"a=rtpmap:103 ISAC/16000\r\n"
|
||||
"a=rtpmap:104 ISAC/32000\r\n"
|
||||
@ -556,6 +586,8 @@ static const char kUnifiedPlanSdpFullString[] =
|
||||
"a=mid:video_content_name_2\r\n"
|
||||
"a=msid:local_stream_2 video_track_id_2\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
|
||||
"inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
|
||||
"a=rtpmap:120 VP8/90000\r\n"
|
||||
"a=ssrc:5 cname:stream_2_cname\r\n"
|
||||
// Video track 3, stream 2.
|
||||
@ -566,6 +598,8 @@ static const char kUnifiedPlanSdpFullString[] =
|
||||
"a=mid:video_content_name_3\r\n"
|
||||
"a=msid:local_stream_2 video_track_id_3\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
|
||||
"inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
|
||||
"a=rtpmap:120 VP8/90000\r\n"
|
||||
"a=ssrc:6 cname:stream_2_cname\r\n";
|
||||
|
||||
@ -607,6 +641,9 @@ static const char kUnifiedPlanSdpFullStringWithSpecialMsid[] =
|
||||
"a=msid:local_stream_1 audio_track_id_1\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=rtcp-rsize\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
|
||||
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
|
||||
"dummy_session_params\r\n"
|
||||
"a=rtpmap:111 opus/48000/2\r\n"
|
||||
"a=rtpmap:103 ISAC/16000\r\n"
|
||||
"a=rtpmap:104 ISAC/32000\r\n"
|
||||
@ -625,6 +662,9 @@ static const char kUnifiedPlanSdpFullStringWithSpecialMsid[] =
|
||||
"a=msid:local_stream_2 audio_track_id_2\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=rtcp-rsize\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
|
||||
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
|
||||
"dummy_session_params\r\n"
|
||||
"a=rtpmap:111 opus/48000/2\r\n"
|
||||
"a=rtpmap:103 ISAC/16000\r\n"
|
||||
"a=rtpmap:104 ISAC/32000\r\n"
|
||||
@ -644,6 +684,9 @@ static const char kUnifiedPlanSdpFullStringWithSpecialMsid[] =
|
||||
"a=msid:- audio_track_id_3\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=rtcp-rsize\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
|
||||
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
|
||||
"dummy_session_params\r\n"
|
||||
"a=rtpmap:111 opus/48000/2\r\n"
|
||||
"a=rtpmap:103 ISAC/16000\r\n"
|
||||
"a=rtpmap:104 ISAC/32000\r\n"
|
||||
@ -683,6 +726,9 @@ static const char kUnifiedPlanSdpFullStringNoSsrc[] =
|
||||
"a=sendrecv\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=rtcp-rsize\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
|
||||
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
|
||||
"dummy_session_params\r\n"
|
||||
"a=rtpmap:111 opus/48000/2\r\n"
|
||||
"a=rtpmap:103 ISAC/16000\r\n"
|
||||
"a=rtpmap:104 ISAC/32000\r\n"
|
||||
@ -706,6 +752,8 @@ static const char kUnifiedPlanSdpFullStringNoSsrc[] =
|
||||
"a=mid:video_content_name\r\n"
|
||||
"a=msid:local_stream_1 video_track_id_1\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
|
||||
"inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
|
||||
"a=rtpmap:120 VP8/90000\r\n"
|
||||
// Audio track 2, stream 2.
|
||||
"m=audio 9 RTP/SAVPF 111 103 104\r\n"
|
||||
@ -717,6 +765,9 @@ static const char kUnifiedPlanSdpFullStringNoSsrc[] =
|
||||
"a=sendrecv\r\n"
|
||||
"a=rtcp-mux\r\n"
|
||||
"a=rtcp-rsize\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
|
||||
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
|
||||
"dummy_session_params\r\n"
|
||||
"a=rtpmap:111 opus/48000/2\r\n"
|
||||
"a=rtpmap:103 ISAC/16000\r\n"
|
||||
"a=rtpmap:104 ISAC/32000\r\n"
|
||||
@ -728,6 +779,8 @@ static const char kUnifiedPlanSdpFullStringNoSsrc[] =
|
||||
"a=mid:video_content_name_2\r\n"
|
||||
"a=msid:local_stream_2 video_track_id_2\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
|
||||
"inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
|
||||
"a=rtpmap:120 VP8/90000\r\n"
|
||||
// Video track 3, stream 2.
|
||||
"m=video 9 RTP/SAVPF 120\r\n"
|
||||
@ -737,6 +790,8 @@ static const char kUnifiedPlanSdpFullStringNoSsrc[] =
|
||||
"a=mid:video_content_name_3\r\n"
|
||||
"a=msid:local_stream_2 video_track_id_3\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
|
||||
"inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
|
||||
"a=rtpmap:120 VP8/90000\r\n";
|
||||
|
||||
// One candidate reference string as per W3c spec.
|
||||
@ -1216,6 +1271,10 @@ class WebRtcSdpTest : public ::testing::Test {
|
||||
AudioContentDescription* audio = new AudioContentDescription();
|
||||
audio->set_rtcp_mux(true);
|
||||
audio->set_rtcp_reduced_size(true);
|
||||
audio->AddCrypto(CryptoParams(
|
||||
1, "AES_CM_128_HMAC_SHA1_32",
|
||||
"inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32",
|
||||
"dummy_session_params"));
|
||||
audio->set_protocol(cricket::kMediaProtocolSavpf);
|
||||
audio->AddCodec(AudioCodec(111, "opus", 48000, 0, 2));
|
||||
audio->AddCodec(AudioCodec(103, "ISAC", 16000, 0, 1));
|
||||
@ -1289,6 +1348,9 @@ class WebRtcSdpTest : public ::testing::Test {
|
||||
// configuration.
|
||||
VideoContentDescription* CreateVideoContentDescription() {
|
||||
VideoContentDescription* video = new VideoContentDescription();
|
||||
video->AddCrypto(CryptoParams(
|
||||
1, "AES_CM_128_HMAC_SHA1_80",
|
||||
"inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32", ""));
|
||||
video->set_protocol(cricket::kMediaProtocolSavpf);
|
||||
video->AddCodec(
|
||||
VideoCodec(120, JsepSessionDescription::kDefaultVideoCodecName));
|
||||
@ -1309,6 +1371,20 @@ class WebRtcSdpTest : public ::testing::Test {
|
||||
// rtcp_reduced_size
|
||||
EXPECT_EQ(cd1->rtcp_reduced_size(), cd2->rtcp_reduced_size());
|
||||
|
||||
// cryptos
|
||||
EXPECT_EQ(cd1->cryptos().size(), cd2->cryptos().size());
|
||||
if (cd1->cryptos().size() != cd2->cryptos().size()) {
|
||||
ADD_FAILURE();
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < cd1->cryptos().size(); ++i) {
|
||||
const CryptoParams c1 = cd1->cryptos().at(i);
|
||||
const CryptoParams c2 = cd2->cryptos().at(i);
|
||||
EXPECT_TRUE(c1.Matches(c2));
|
||||
EXPECT_EQ(c1.key_params, c2.key_params);
|
||||
EXPECT_EQ(c1.session_params, c2.session_params);
|
||||
}
|
||||
|
||||
// protocol
|
||||
// Use an equivalence class here, for old and new versions of the
|
||||
// protocol description.
|
||||
@ -1486,11 +1562,6 @@ class WebRtcSdpTest : public ::testing::Test {
|
||||
const JsepSessionDescription& desc2) {
|
||||
EXPECT_EQ(desc1.session_id(), desc2.session_id());
|
||||
EXPECT_EQ(desc1.session_version(), desc2.session_version());
|
||||
EXPECT_TRUE(desc1.description());
|
||||
EXPECT_TRUE(desc2.description());
|
||||
if (!desc1.description() || !desc2.description()) {
|
||||
return false;
|
||||
}
|
||||
CompareSessionDescription(*desc1.description(), *desc2.description());
|
||||
if (desc1.number_of_mediasections() != desc2.number_of_mediasections())
|
||||
return false;
|
||||
@ -1604,6 +1675,11 @@ class WebRtcSdpTest : public ::testing::Test {
|
||||
absl::WrapUnique(video_desc_));
|
||||
}
|
||||
|
||||
void RemoveCryptos() {
|
||||
audio_desc_->set_cryptos(std::vector<CryptoParams>());
|
||||
video_desc_->set_cryptos(std::vector<CryptoParams>());
|
||||
}
|
||||
|
||||
// Removes everything in StreamParams from the session description that is
|
||||
// used for a=ssrc lines.
|
||||
void RemoveSsrcSignalingFromStreamParams() {
|
||||
@ -2007,7 +2083,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionEmpty) {
|
||||
EXPECT_EQ("", webrtc::SdpSerialize(jdesc_empty));
|
||||
}
|
||||
|
||||
// This tests serialization of SDP with a=fingerprint, as would be
|
||||
// This tests serialization of SDP with a=crypto and a=fingerprint, as would be
|
||||
// the case in a DTLS offer.
|
||||
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprint) {
|
||||
AddFingerprint();
|
||||
@ -2026,6 +2102,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprint) {
|
||||
// be the case in a DTLS answer.
|
||||
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprintNoCryptos) {
|
||||
AddFingerprint();
|
||||
RemoveCryptos();
|
||||
JsepSessionDescription jdesc_with_fingerprint(kDummyType);
|
||||
MakeDescriptionWithoutCandidates(&jdesc_with_fingerprint);
|
||||
std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint);
|
||||
@ -3178,6 +3255,8 @@ TEST_F(WebRtcSdpTest, DeserializeSdpWithInvalidAttributeValue) {
|
||||
// ssrc
|
||||
ExpectParseFailure("a=ssrc:1", "a=ssrc:badvalue");
|
||||
ExpectParseFailure("a=ssrc-group:FEC 2 3", "a=ssrc-group:FEC badvalue 3");
|
||||
// crypto
|
||||
ExpectParseFailure("a=crypto:1 ", "a=crypto:badvalue ");
|
||||
// rtpmap
|
||||
ExpectParseFailure("a=rtpmap:111 ", "a=rtpmap:badvalue ");
|
||||
ExpectParseFailure("opus/48000/2", "opus/badvalue/2");
|
||||
@ -3728,7 +3807,7 @@ TEST_F(WebRtcSdpTest, DeserializeUnifiedPlanSessionDescription) {
|
||||
MakeUnifiedPlanDescription();
|
||||
|
||||
JsepSessionDescription deserialized_description(kDummyType);
|
||||
ASSERT_TRUE(
|
||||
EXPECT_TRUE(
|
||||
SdpDeserialize(kUnifiedPlanSdpFullString, &deserialized_description));
|
||||
|
||||
EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
|
||||
|
||||
@ -152,10 +152,13 @@ WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
|
||||
RTC_DCHECK(signaling_thread_);
|
||||
|
||||
if (!dtls_enabled) {
|
||||
RTC_LOG(LS_INFO) << "DTLS is disabled, no encryption applied";
|
||||
SetSdesPolicy(cricket::SEC_REQUIRED);
|
||||
RTC_LOG(LS_VERBOSE) << "DTLS-SRTP disabled.";
|
||||
return;
|
||||
}
|
||||
RTC_DCHECK(certificate || cert_generator_);
|
||||
|
||||
// SRTP-SDES is disabled if DTLS is on.
|
||||
SetSdesPolicy(cricket::SEC_DISABLED);
|
||||
if (certificate) {
|
||||
// Use `certificate`.
|
||||
certificate_request_state_ = CERTIFICATE_WAITING;
|
||||
@ -286,6 +289,15 @@ void WebRtcSessionDescriptionFactory::CreateAnswer(
|
||||
}
|
||||
}
|
||||
|
||||
void WebRtcSessionDescriptionFactory::SetSdesPolicy(
|
||||
cricket::SecurePolicy secure_policy) {
|
||||
session_desc_factory_.set_secure(secure_policy);
|
||||
}
|
||||
|
||||
cricket::SecurePolicy WebRtcSessionDescriptionFactory::SdesPolicy() const {
|
||||
return session_desc_factory_.secure();
|
||||
}
|
||||
|
||||
void WebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg) {
|
||||
switch (msg->message_id) {
|
||||
case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: {
|
||||
@ -482,6 +494,7 @@ void WebRtcSessionDescriptionFactory::SetCertificate(
|
||||
on_certificate_ready_(certificate);
|
||||
|
||||
transport_desc_factory_.set_certificate(certificate);
|
||||
transport_desc_factory_.set_secure(cricket::SEC_ENABLED);
|
||||
|
||||
while (!create_session_description_requests_.empty()) {
|
||||
if (create_session_description_requests_.front().type ==
|
||||
|
||||
@ -104,6 +104,9 @@ class WebRtcSessionDescriptionFactory : public rtc::MessageHandler,
|
||||
void CreateAnswer(CreateSessionDescriptionObserver* observer,
|
||||
const cricket::MediaSessionOptions& session_options);
|
||||
|
||||
void SetSdesPolicy(cricket::SecurePolicy secure_policy);
|
||||
cricket::SecurePolicy SdesPolicy() const;
|
||||
|
||||
void set_enable_encrypted_rtp_header_extensions(bool enable) {
|
||||
session_desc_factory_.set_enable_encrypted_rtp_header_extensions(enable);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user