dtls: allow dtls role to change during DTLS restart
which is characterized by a change in remote fingerprint and causes a new DTLS handshake. This allows renegotiating the client/server role as well. Spec guidance is provided by https://www.rfc-editor.org/rfc/rfc5763#section-6.6 BUG=webrtc:5768 Change-Id: I0e8630c0c5907cc92720762a4320ad21a6190d28 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/271680 Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Philipp Hancke <phancke@microsoft.com> Cr-Commit-Position: refs/heads/main@{#37821}
This commit is contained in:

committed by
WebRTC LUCI CQ

parent
780e2e356f
commit
02b5f3c9c1
@ -227,6 +227,34 @@ bool DtlsTransport::GetSslCipherSuite(int* cipher) {
|
||||
return dtls_->GetSslCipherSuite(cipher);
|
||||
}
|
||||
|
||||
webrtc::RTCError DtlsTransport::SetRemoteParameters(
|
||||
absl::string_view digest_alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len,
|
||||
absl::optional<rtc::SSLRole> role) {
|
||||
rtc::Buffer remote_fingerprint_value(digest, digest_len);
|
||||
bool is_dtls_restart =
|
||||
dtls_active_ && remote_fingerprint_value_ != remote_fingerprint_value;
|
||||
// Set SSL role. Role must be set before fingerprint is applied, which
|
||||
// initiates DTLS setup.
|
||||
if (role) {
|
||||
if (is_dtls_restart) {
|
||||
dtls_role_ = *role;
|
||||
} else {
|
||||
if (!SetDtlsRole(*role)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to set SSL role for the transport.");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Apply remote fingerprint.
|
||||
if (!SetRemoteFingerprint(digest_alg, digest, digest_len)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to apply remote fingerprint.");
|
||||
}
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
|
||||
bool DtlsTransport::SetRemoteFingerprint(absl::string_view digest_alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len) {
|
||||
|
@ -133,12 +133,12 @@ class DtlsTransport : public DtlsTransportInternal {
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override;
|
||||
|
||||
// SetRemoteFingerprint must be called after SetLocalCertificate, and any
|
||||
// other methods like SetDtlsRole. It's what triggers the actual DTLS setup.
|
||||
// TODO(deadbeef): Rename to "Start" like in ORTC?
|
||||
bool SetRemoteFingerprint(absl::string_view digest_alg,
|
||||
// SetRemoteParameters must be called after SetLocalCertificate.
|
||||
webrtc::RTCError SetRemoteParameters(
|
||||
absl::string_view digest_alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len) override;
|
||||
size_t digest_len,
|
||||
absl::optional<rtc::SSLRole> role) override;
|
||||
|
||||
// Called to send a packet (via DTLS, if turned on).
|
||||
int SendPacket(const char* data,
|
||||
@ -226,6 +226,13 @@ class DtlsTransport : public DtlsTransportInternal {
|
||||
// Sets the DTLS state, signaling if necessary.
|
||||
void set_dtls_state(webrtc::DtlsTransportState state);
|
||||
|
||||
// SetRemoteFingerprint must be called after SetLocalCertificate, and any
|
||||
// other methods like SetDtlsRole. It's what triggers the actual DTLS setup.
|
||||
// TODO(deadbeef): Rename to "Start" like in ORTC?
|
||||
bool SetRemoteFingerprint(absl::string_view digest_alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len);
|
||||
|
||||
webrtc::SequenceChecker thread_checker_;
|
||||
|
||||
const int component_;
|
||||
|
@ -89,10 +89,12 @@ class DtlsTransportInternal : public rtc::PacketTransportInternal {
|
||||
uint8_t* result,
|
||||
size_t result_len) = 0;
|
||||
|
||||
// Set DTLS remote fingerprint. Must be after local identity set.
|
||||
virtual bool SetRemoteFingerprint(absl::string_view digest_alg,
|
||||
// Set DTLS remote fingerprint and role. Must be after local identity set.
|
||||
virtual webrtc::RTCError SetRemoteParameters(
|
||||
absl::string_view digest_alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len) = 0;
|
||||
size_t digest_len,
|
||||
absl::optional<rtc::SSLRole> role) = 0;
|
||||
|
||||
ABSL_DEPRECATED("Set the max version via construction.")
|
||||
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) {
|
||||
|
@ -56,11 +56,14 @@ void SetRemoteFingerprintFromCert(
|
||||
if (modify_digest) {
|
||||
++fingerprint->digest.MutableData()[0];
|
||||
}
|
||||
// Even if digest is verified to be incorrect, should fail asynchrnously.
|
||||
EXPECT_TRUE(transport->SetRemoteFingerprint(
|
||||
// Even if digest is verified to be incorrect, should fail asynchronously.
|
||||
EXPECT_TRUE(
|
||||
transport
|
||||
->SetRemoteParameters(
|
||||
fingerprint->algorithm,
|
||||
reinterpret_cast<const uint8_t*>(fingerprint->digest.data()),
|
||||
fingerprint->digest.size()));
|
||||
fingerprint->digest.size(), absl::nullopt)
|
||||
.ok());
|
||||
}
|
||||
|
||||
class DtlsTestClient : public sigslot::has_slots<> {
|
||||
|
@ -141,12 +141,15 @@ class FakeDtlsTransport : public DtlsTransportInternal {
|
||||
const rtc::SSLFingerprint& dtls_fingerprint() const {
|
||||
return dtls_fingerprint_;
|
||||
}
|
||||
bool SetRemoteFingerprint(absl::string_view alg,
|
||||
webrtc::RTCError SetRemoteParameters(absl::string_view alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len) override {
|
||||
dtls_fingerprint_ =
|
||||
rtc::SSLFingerprint(alg, rtc::MakeArrayView(digest, digest_len));
|
||||
return true;
|
||||
size_t digest_len,
|
||||
absl::optional<rtc::SSLRole> role) {
|
||||
if (role) {
|
||||
SetDtlsRole(*role);
|
||||
}
|
||||
SetRemoteFingerprint(alg, digest, digest_len);
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
bool SetDtlsRole(rtc::SSLRole role) override {
|
||||
dtls_role_ = std::move(role);
|
||||
@ -283,6 +286,14 @@ class FakeDtlsTransport : public DtlsTransportInternal {
|
||||
SignalNetworkRouteChanged(network_route);
|
||||
}
|
||||
|
||||
bool SetRemoteFingerprint(absl::string_view alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len) {
|
||||
dtls_fingerprint_ =
|
||||
rtc::SSLFingerprint(alg, rtc::MakeArrayView(digest, digest_len));
|
||||
return true;
|
||||
}
|
||||
|
||||
FakeIceTransport* ice_transport_;
|
||||
std::unique_ptr<FakeIceTransport> owned_ice_transport_;
|
||||
std::string transport_name_;
|
||||
|
@ -420,21 +420,9 @@ webrtc::RTCError JsepTransport::SetNegotiatedDtlsParameters(
|
||||
absl::optional<rtc::SSLRole> dtls_role,
|
||||
rtc::SSLFingerprint* remote_fingerprint) {
|
||||
RTC_DCHECK(dtls_transport);
|
||||
// Set SSL role. Role must be set before fingerprint is applied, which
|
||||
// initiates DTLS setup.
|
||||
if (dtls_role && !dtls_transport->SetDtlsRole(*dtls_role)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to set SSL role for the transport.");
|
||||
}
|
||||
// Apply remote fingerprint.
|
||||
if (!remote_fingerprint ||
|
||||
!dtls_transport->SetRemoteFingerprint(
|
||||
return dtls_transport->SetRemoteParameters(
|
||||
remote_fingerprint->algorithm, remote_fingerprint->digest.cdata(),
|
||||
remote_fingerprint->digest.size())) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to apply remote fingerprint.");
|
||||
}
|
||||
return webrtc::RTCError::OK();
|
||||
remote_fingerprint->digest.size(), dtls_role);
|
||||
}
|
||||
|
||||
bool JsepTransport::SetRtcpMux(bool enable,
|
||||
|
@ -881,6 +881,61 @@ TEST_F(JsepTransport2Test, RemoteOfferThatChangesNegotiatedDtlsRole) {
|
||||
.ok());
|
||||
}
|
||||
|
||||
// Test that a remote offer which changes both fingerprint and role is accepted.
|
||||
TEST_F(JsepTransport2Test, RemoteOfferThatChangesFingerprintAndDtlsRole) {
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
|
||||
rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("testing1", rtc::KT_ECDSA));
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
|
||||
rtc::RTCCertificate::Create(
|
||||
rtc::SSLIdentity::Create("testing2", rtc::KT_ECDSA));
|
||||
bool rtcp_mux_enabled = true;
|
||||
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
|
||||
jsep_transport_->SetLocalCertificate(certificate);
|
||||
|
||||
JsepTransportDescription remote_desc =
|
||||
MakeJsepTransportDescription(rtcp_mux_enabled, kIceUfrag1, kIcePwd1,
|
||||
certificate, CONNECTIONROLE_ACTPASS);
|
||||
JsepTransportDescription remote_desc2 =
|
||||
MakeJsepTransportDescription(rtcp_mux_enabled, kIceUfrag1, kIcePwd1,
|
||||
certificate2, CONNECTIONROLE_ACTPASS);
|
||||
|
||||
JsepTransportDescription local_desc =
|
||||
MakeJsepTransportDescription(rtcp_mux_enabled, kIceUfrag2, kIcePwd2,
|
||||
certificate, CONNECTIONROLE_ACTIVE);
|
||||
|
||||
// Normal initial offer/answer with "actpass" in the offer and "active" in
|
||||
// the answer.
|
||||
ASSERT_TRUE(
|
||||
jsep_transport_
|
||||
->SetRemoteJsepTransportDescription(remote_desc, SdpType::kOffer)
|
||||
.ok());
|
||||
ASSERT_TRUE(
|
||||
jsep_transport_
|
||||
->SetLocalJsepTransportDescription(local_desc, SdpType::kAnswer)
|
||||
.ok());
|
||||
|
||||
// Sanity check that role was actually negotiated.
|
||||
absl::optional<rtc::SSLRole> role = jsep_transport_->GetDtlsRole();
|
||||
ASSERT_TRUE(role);
|
||||
EXPECT_EQ(rtc::SSL_CLIENT, *role);
|
||||
|
||||
// Subsequent exchange with new remote fingerprint and different role.
|
||||
local_desc.transport_desc.connection_role = CONNECTIONROLE_PASSIVE;
|
||||
EXPECT_TRUE(
|
||||
jsep_transport_
|
||||
->SetRemoteJsepTransportDescription(remote_desc2, SdpType::kOffer)
|
||||
.ok());
|
||||
EXPECT_TRUE(
|
||||
jsep_transport_
|
||||
->SetLocalJsepTransportDescription(local_desc, SdpType::kAnswer)
|
||||
.ok());
|
||||
|
||||
role = jsep_transport_->GetDtlsRole();
|
||||
ASSERT_TRUE(role);
|
||||
EXPECT_EQ(rtc::SSL_SERVER, *role);
|
||||
}
|
||||
|
||||
// Testing that a legacy client that doesn't use the setup attribute will be
|
||||
// interpreted as having an active role.
|
||||
TEST_F(JsepTransport2Test, DtlsSetupWithLegacyAsAnswerer) {
|
||||
@ -912,7 +967,7 @@ TEST_F(JsepTransport2Test, DtlsSetupWithLegacyAsAnswerer) {
|
||||
|
||||
absl::optional<rtc::SSLRole> role = jsep_transport_->GetDtlsRole();
|
||||
ASSERT_TRUE(role);
|
||||
// Since legacy answer ommitted setup atribute, and we offered actpass, we
|
||||
// Since legacy answer omitted setup atribute, and we offered actpass, we
|
||||
// should act as passive (server).
|
||||
EXPECT_EQ(rtc::SSL_SERVER, *role);
|
||||
}
|
||||
|
Reference in New Issue
Block a user