webrtc_m130/pc/dtlssrtptransport.cc
Jonas Olsson 635474e3d5 Compute RTCConnectionState and RTCIceConnectionState.
Compute these states in jseptransportController and store them. Eventually they should be passed on to the peer connection observer and exposed in the blink layer.

Bug: webrtc:9308
Change-Id: Ifdec39c24a607fcb8211c4acf6b9704eaff371b1
Reviewed-on: https://webrtc-review.googlesource.com/c/103506
Commit-Queue: Jonas Olsson <jonasolsson@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25288}
2018-10-22 11:33:17 +00:00

320 lines
11 KiB
C++

/*
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "pc/dtlssrtptransport.h"
#include <memory>
#include <string>
#include <utility>
#include "media/base/rtputils.h"
#include "rtc_base/sslstreamadapter.h"
namespace {
// Value specified in RFC 5764.
static const char kDtlsSrtpExporterLabel[] = "EXTRACTOR-dtls_srtp";
} // namespace
namespace webrtc {
DtlsSrtpTransport::DtlsSrtpTransport(bool rtcp_mux_enabled)
: SrtpTransport(rtcp_mux_enabled) {}
void DtlsSrtpTransport::SetDtlsTransports(
cricket::DtlsTransportInternal* rtp_dtls_transport,
cricket::DtlsTransportInternal* rtcp_dtls_transport) {
// Transport names should be the same.
if (rtp_dtls_transport && rtcp_dtls_transport) {
RTC_DCHECK(rtp_dtls_transport->transport_name() ==
rtcp_dtls_transport->transport_name());
}
// When using DTLS-SRTP, we must reset the SrtpTransport every time the
// DtlsTransport changes and wait until the DTLS handshake is complete to set
// the newly negotiated parameters.
// If |active_reset_srtp_params_| is true, intentionally reset the SRTP
// parameter even though the DtlsTransport may not change.
if (IsSrtpActive() && (rtp_dtls_transport != rtp_dtls_transport_ ||
active_reset_srtp_params_)) {
ResetParams();
}
const std::string transport_name =
rtp_dtls_transport ? rtp_dtls_transport->transport_name() : "null";
if (rtcp_dtls_transport && rtcp_dtls_transport != rtcp_dtls_transport_) {
// This would only be possible if using BUNDLE but not rtcp-mux, which isn't
// allowed according to the BUNDLE spec.
RTC_CHECK(!(IsSrtpActive()))
<< "Setting RTCP for DTLS/SRTP after the DTLS is active "
"should never happen.";
}
RTC_LOG(LS_INFO) << "Setting RTCP Transport on " << transport_name
<< " transport " << rtcp_dtls_transport;
SetRtcpDtlsTransport(rtcp_dtls_transport);
SetRtcpPacketTransport(rtcp_dtls_transport);
RTC_LOG(LS_INFO) << "Setting RTP Transport on " << transport_name
<< " transport " << rtp_dtls_transport;
SetRtpDtlsTransport(rtp_dtls_transport);
SetRtpPacketTransport(rtp_dtls_transport);
MaybeSetupDtlsSrtp();
}
void DtlsSrtpTransport::SetRtcpMuxEnabled(bool enable) {
SrtpTransport::SetRtcpMuxEnabled(enable);
if (enable) {
MaybeSetupDtlsSrtp();
}
}
void DtlsSrtpTransport::UpdateSendEncryptedHeaderExtensionIds(
const std::vector<int>& send_extension_ids) {
if (send_extension_ids_ == send_extension_ids) {
return;
}
send_extension_ids_.emplace(send_extension_ids);
if (DtlsHandshakeCompleted()) {
// Reset the crypto parameters to update the send extension IDs.
SetupRtpDtlsSrtp();
}
}
void DtlsSrtpTransport::UpdateRecvEncryptedHeaderExtensionIds(
const std::vector<int>& recv_extension_ids) {
if (recv_extension_ids_ == recv_extension_ids) {
return;
}
recv_extension_ids_.emplace(recv_extension_ids);
if (DtlsHandshakeCompleted()) {
// Reset the crypto parameters to update the receive extension IDs.
SetupRtpDtlsSrtp();
}
}
bool DtlsSrtpTransport::IsDtlsActive() {
auto rtcp_dtls_transport =
rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_;
return (rtp_dtls_transport_ && rtp_dtls_transport_->IsDtlsActive() &&
(!rtcp_dtls_transport || rtcp_dtls_transport->IsDtlsActive()));
}
bool DtlsSrtpTransport::IsDtlsConnected() {
auto rtcp_dtls_transport =
rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_;
return (rtp_dtls_transport_ &&
rtp_dtls_transport_->dtls_state() ==
cricket::DTLS_TRANSPORT_CONNECTED &&
(!rtcp_dtls_transport || rtcp_dtls_transport->dtls_state() ==
cricket::DTLS_TRANSPORT_CONNECTED));
}
bool DtlsSrtpTransport::IsDtlsWritable() {
auto rtcp_packet_transport =
rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_;
return rtp_dtls_transport_ && rtp_dtls_transport_->writable() &&
(!rtcp_packet_transport || rtcp_packet_transport->writable());
}
bool DtlsSrtpTransport::DtlsHandshakeCompleted() {
return IsDtlsActive() && IsDtlsConnected();
}
void DtlsSrtpTransport::MaybeSetupDtlsSrtp() {
if (IsSrtpActive() || !IsDtlsWritable()) {
return;
}
SetupRtpDtlsSrtp();
if (!rtcp_mux_enabled() && rtcp_dtls_transport_) {
SetupRtcpDtlsSrtp();
}
}
void DtlsSrtpTransport::SetupRtpDtlsSrtp() {
// Use an empty encrypted header extension ID vector if not set. This could
// happen when the DTLS handshake is completed before processing the
// Offer/Answer which contains the encrypted header extension IDs.
std::vector<int> send_extension_ids;
std::vector<int> recv_extension_ids;
if (send_extension_ids_) {
send_extension_ids = *send_extension_ids_;
}
if (recv_extension_ids_) {
recv_extension_ids = *recv_extension_ids_;
}
int selected_crypto_suite;
rtc::ZeroOnFreeBuffer<unsigned char> send_key;
rtc::ZeroOnFreeBuffer<unsigned char> recv_key;
if (!ExtractParams(rtp_dtls_transport_, &selected_crypto_suite, &send_key,
&recv_key) ||
!SetRtpParams(selected_crypto_suite, &send_key[0],
static_cast<int>(send_key.size()), send_extension_ids,
selected_crypto_suite, &recv_key[0],
static_cast<int>(recv_key.size()), recv_extension_ids)) {
SignalDtlsSrtpSetupFailure(this, /*rtcp=*/false);
RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTP failed";
}
}
void DtlsSrtpTransport::SetupRtcpDtlsSrtp() {
// Return if the DTLS-SRTP is active because the encrypted header extension
// IDs don't need to be updated for RTCP and the crypto params don't need to
// be reset.
if (IsSrtpActive()) {
return;
}
std::vector<int> send_extension_ids;
std::vector<int> recv_extension_ids;
if (send_extension_ids_) {
send_extension_ids = *send_extension_ids_;
}
if (recv_extension_ids_) {
recv_extension_ids = *recv_extension_ids_;
}
int selected_crypto_suite;
rtc::ZeroOnFreeBuffer<unsigned char> rtcp_send_key;
rtc::ZeroOnFreeBuffer<unsigned char> rtcp_recv_key;
if (!ExtractParams(rtcp_dtls_transport_, &selected_crypto_suite,
&rtcp_send_key, &rtcp_recv_key) ||
!SetRtcpParams(selected_crypto_suite, &rtcp_send_key[0],
static_cast<int>(rtcp_send_key.size()), send_extension_ids,
selected_crypto_suite, &rtcp_recv_key[0],
static_cast<int>(rtcp_recv_key.size()),
recv_extension_ids)) {
SignalDtlsSrtpSetupFailure(this, /*rtcp=*/true);
RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTCP failed";
}
}
bool DtlsSrtpTransport::ExtractParams(
cricket::DtlsTransportInternal* dtls_transport,
int* selected_crypto_suite,
rtc::ZeroOnFreeBuffer<unsigned char>* send_key,
rtc::ZeroOnFreeBuffer<unsigned char>* recv_key) {
if (!dtls_transport || !dtls_transport->IsDtlsActive()) {
return false;
}
if (!dtls_transport->GetSrtpCryptoSuite(selected_crypto_suite)) {
RTC_LOG(LS_ERROR) << "No DTLS-SRTP selected crypto suite";
return false;
}
RTC_LOG(LS_INFO) << "Extracting keys from transport: "
<< dtls_transport->transport_name();
int key_len;
int salt_len;
if (!rtc::GetSrtpKeyAndSaltLengths((*selected_crypto_suite), &key_len,
&salt_len)) {
RTC_LOG(LS_ERROR) << "Unknown DTLS-SRTP crypto suite"
<< selected_crypto_suite;
return false;
}
// OK, we're now doing DTLS (RFC 5764)
rtc::ZeroOnFreeBuffer<unsigned char> dtls_buffer(key_len * 2 + salt_len * 2);
// RFC 5705 exporter using the RFC 5764 parameters
if (!dtls_transport->ExportKeyingMaterial(kDtlsSrtpExporterLabel, NULL, 0,
false, &dtls_buffer[0],
dtls_buffer.size())) {
RTC_LOG(LS_WARNING) << "DTLS-SRTP key export failed";
RTC_NOTREACHED(); // This should never happen
return false;
}
// Sync up the keys with the DTLS-SRTP interface
rtc::ZeroOnFreeBuffer<unsigned char> client_write_key(key_len + salt_len);
rtc::ZeroOnFreeBuffer<unsigned char> server_write_key(key_len + salt_len);
size_t offset = 0;
memcpy(&client_write_key[0], &dtls_buffer[offset], key_len);
offset += key_len;
memcpy(&server_write_key[0], &dtls_buffer[offset], key_len);
offset += key_len;
memcpy(&client_write_key[key_len], &dtls_buffer[offset], salt_len);
offset += salt_len;
memcpy(&server_write_key[key_len], &dtls_buffer[offset], salt_len);
rtc::SSLRole role;
if (!dtls_transport->GetDtlsRole(&role)) {
RTC_LOG(LS_WARNING) << "Failed to get the DTLS role.";
return false;
}
if (role == rtc::SSL_SERVER) {
*send_key = std::move(server_write_key);
*recv_key = std::move(client_write_key);
} else {
*send_key = std::move(client_write_key);
*recv_key = std::move(server_write_key);
}
return true;
}
void DtlsSrtpTransport::SetDtlsTransport(
cricket::DtlsTransportInternal* new_dtls_transport,
cricket::DtlsTransportInternal** old_dtls_transport) {
if (*old_dtls_transport == new_dtls_transport) {
return;
}
if (*old_dtls_transport) {
(*old_dtls_transport)->SignalDtlsState.disconnect(this);
}
*old_dtls_transport = new_dtls_transport;
if (new_dtls_transport) {
new_dtls_transport->SignalDtlsState.connect(
this, &DtlsSrtpTransport::OnDtlsState);
}
}
void DtlsSrtpTransport::SetRtpDtlsTransport(
cricket::DtlsTransportInternal* rtp_dtls_transport) {
SetDtlsTransport(rtp_dtls_transport, &rtp_dtls_transport_);
}
void DtlsSrtpTransport::SetRtcpDtlsTransport(
cricket::DtlsTransportInternal* rtcp_dtls_transport) {
SetDtlsTransport(rtcp_dtls_transport, &rtcp_dtls_transport_);
}
void DtlsSrtpTransport::OnDtlsState(cricket::DtlsTransportInternal* transport,
cricket::DtlsTransportState state) {
RTC_DCHECK(transport == rtp_dtls_transport_ ||
transport == rtcp_dtls_transport_);
SignalDtlsStateChange();
if (state != cricket::DTLS_TRANSPORT_CONNECTED) {
ResetParams();
return;
}
MaybeSetupDtlsSrtp();
}
void DtlsSrtpTransport::OnWritableState(
rtc::PacketTransportInternal* packet_transport) {
MaybeSetupDtlsSrtp();
}
} // namespace webrtc