1 /*
2 * Copyright 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "pc/dtls_srtp_transport.h"
12
13 #include <string.h>
14
15 #include <string>
16 #include <utility>
17
18 #include "rtc_base/checks.h"
19 #include "rtc_base/logging.h"
20 #include "rtc_base/ssl_stream_adapter.h"
21
22 namespace {
23 // Value specified in RFC 5764.
24 static const char kDtlsSrtpExporterLabel[] = "EXTRACTOR-dtls_srtp";
25 } // namespace
26
27 namespace webrtc {
28
DtlsSrtpTransport(bool rtcp_mux_enabled)29 DtlsSrtpTransport::DtlsSrtpTransport(bool rtcp_mux_enabled)
30 : SrtpTransport(rtcp_mux_enabled) {}
31
SetDtlsTransports(cricket::DtlsTransportInternal * rtp_dtls_transport,cricket::DtlsTransportInternal * rtcp_dtls_transport)32 void DtlsSrtpTransport::SetDtlsTransports(
33 cricket::DtlsTransportInternal* rtp_dtls_transport,
34 cricket::DtlsTransportInternal* rtcp_dtls_transport) {
35 // Transport names should be the same.
36 if (rtp_dtls_transport && rtcp_dtls_transport) {
37 RTC_DCHECK(rtp_dtls_transport->transport_name() ==
38 rtcp_dtls_transport->transport_name());
39 }
40
41 // When using DTLS-SRTP, we must reset the SrtpTransport every time the
42 // DtlsTransport changes and wait until the DTLS handshake is complete to set
43 // the newly negotiated parameters.
44 // If |active_reset_srtp_params_| is true, intentionally reset the SRTP
45 // parameter even though the DtlsTransport may not change.
46 if (IsSrtpActive() && (rtp_dtls_transport != rtp_dtls_transport_ ||
47 active_reset_srtp_params_)) {
48 ResetParams();
49 }
50
51 const std::string transport_name =
52 rtp_dtls_transport ? rtp_dtls_transport->transport_name() : "null";
53
54 if (rtcp_dtls_transport && rtcp_dtls_transport != rtcp_dtls_transport_) {
55 // This would only be possible if using BUNDLE but not rtcp-mux, which isn't
56 // allowed according to the BUNDLE spec.
57 RTC_CHECK(!(IsSrtpActive()))
58 << "Setting RTCP for DTLS/SRTP after the DTLS is active "
59 "should never happen.";
60 }
61
62 RTC_LOG(LS_INFO) << "Setting RTCP Transport on " << transport_name
63 << " transport " << rtcp_dtls_transport;
64 SetRtcpDtlsTransport(rtcp_dtls_transport);
65 SetRtcpPacketTransport(rtcp_dtls_transport);
66
67 RTC_LOG(LS_INFO) << "Setting RTP Transport on " << transport_name
68 << " transport " << rtp_dtls_transport;
69 SetRtpDtlsTransport(rtp_dtls_transport);
70 SetRtpPacketTransport(rtp_dtls_transport);
71
72 MaybeSetupDtlsSrtp();
73 }
74
SetRtcpMuxEnabled(bool enable)75 void DtlsSrtpTransport::SetRtcpMuxEnabled(bool enable) {
76 SrtpTransport::SetRtcpMuxEnabled(enable);
77 if (enable) {
78 MaybeSetupDtlsSrtp();
79 }
80 }
81
UpdateSendEncryptedHeaderExtensionIds(const std::vector<int> & send_extension_ids)82 void DtlsSrtpTransport::UpdateSendEncryptedHeaderExtensionIds(
83 const std::vector<int>& send_extension_ids) {
84 if (send_extension_ids_ == send_extension_ids) {
85 return;
86 }
87 send_extension_ids_.emplace(send_extension_ids);
88 if (DtlsHandshakeCompleted()) {
89 // Reset the crypto parameters to update the send extension IDs.
90 SetupRtpDtlsSrtp();
91 }
92 }
93
UpdateRecvEncryptedHeaderExtensionIds(const std::vector<int> & recv_extension_ids)94 void DtlsSrtpTransport::UpdateRecvEncryptedHeaderExtensionIds(
95 const std::vector<int>& recv_extension_ids) {
96 if (recv_extension_ids_ == recv_extension_ids) {
97 return;
98 }
99 recv_extension_ids_.emplace(recv_extension_ids);
100 if (DtlsHandshakeCompleted()) {
101 // Reset the crypto parameters to update the receive extension IDs.
102 SetupRtpDtlsSrtp();
103 }
104 }
105
IsDtlsActive()106 bool DtlsSrtpTransport::IsDtlsActive() {
107 auto rtcp_dtls_transport =
108 rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_;
109 return (rtp_dtls_transport_ && rtp_dtls_transport_->IsDtlsActive() &&
110 (!rtcp_dtls_transport || rtcp_dtls_transport->IsDtlsActive()));
111 }
112
IsDtlsConnected()113 bool DtlsSrtpTransport::IsDtlsConnected() {
114 auto rtcp_dtls_transport =
115 rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_;
116 return (rtp_dtls_transport_ &&
117 rtp_dtls_transport_->dtls_state() ==
118 cricket::DTLS_TRANSPORT_CONNECTED &&
119 (!rtcp_dtls_transport || rtcp_dtls_transport->dtls_state() ==
120 cricket::DTLS_TRANSPORT_CONNECTED));
121 }
122
IsDtlsWritable()123 bool DtlsSrtpTransport::IsDtlsWritable() {
124 auto rtcp_packet_transport =
125 rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_;
126 return rtp_dtls_transport_ && rtp_dtls_transport_->writable() &&
127 (!rtcp_packet_transport || rtcp_packet_transport->writable());
128 }
129
DtlsHandshakeCompleted()130 bool DtlsSrtpTransport::DtlsHandshakeCompleted() {
131 return IsDtlsActive() && IsDtlsConnected();
132 }
133
MaybeSetupDtlsSrtp()134 void DtlsSrtpTransport::MaybeSetupDtlsSrtp() {
135 if (IsSrtpActive() || !IsDtlsWritable()) {
136 return;
137 }
138
139 SetupRtpDtlsSrtp();
140
141 if (!rtcp_mux_enabled() && rtcp_dtls_transport_) {
142 SetupRtcpDtlsSrtp();
143 }
144 }
145
SetupRtpDtlsSrtp()146 void DtlsSrtpTransport::SetupRtpDtlsSrtp() {
147 // Use an empty encrypted header extension ID vector if not set. This could
148 // happen when the DTLS handshake is completed before processing the
149 // Offer/Answer which contains the encrypted header extension IDs.
150 std::vector<int> send_extension_ids;
151 std::vector<int> recv_extension_ids;
152 if (send_extension_ids_) {
153 send_extension_ids = *send_extension_ids_;
154 }
155 if (recv_extension_ids_) {
156 recv_extension_ids = *recv_extension_ids_;
157 }
158
159 int selected_crypto_suite;
160 rtc::ZeroOnFreeBuffer<unsigned char> send_key;
161 rtc::ZeroOnFreeBuffer<unsigned char> recv_key;
162
163 if (!ExtractParams(rtp_dtls_transport_, &selected_crypto_suite, &send_key,
164 &recv_key) ||
165 !SetRtpParams(selected_crypto_suite, &send_key[0],
166 static_cast<int>(send_key.size()), send_extension_ids,
167 selected_crypto_suite, &recv_key[0],
168 static_cast<int>(recv_key.size()), recv_extension_ids)) {
169 RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTP failed";
170 }
171 }
172
SetupRtcpDtlsSrtp()173 void DtlsSrtpTransport::SetupRtcpDtlsSrtp() {
174 // Return if the DTLS-SRTP is active because the encrypted header extension
175 // IDs don't need to be updated for RTCP and the crypto params don't need to
176 // be reset.
177 if (IsSrtpActive()) {
178 return;
179 }
180
181 std::vector<int> send_extension_ids;
182 std::vector<int> recv_extension_ids;
183 if (send_extension_ids_) {
184 send_extension_ids = *send_extension_ids_;
185 }
186 if (recv_extension_ids_) {
187 recv_extension_ids = *recv_extension_ids_;
188 }
189
190 int selected_crypto_suite;
191 rtc::ZeroOnFreeBuffer<unsigned char> rtcp_send_key;
192 rtc::ZeroOnFreeBuffer<unsigned char> rtcp_recv_key;
193 if (!ExtractParams(rtcp_dtls_transport_, &selected_crypto_suite,
194 &rtcp_send_key, &rtcp_recv_key) ||
195 !SetRtcpParams(selected_crypto_suite, &rtcp_send_key[0],
196 static_cast<int>(rtcp_send_key.size()), send_extension_ids,
197 selected_crypto_suite, &rtcp_recv_key[0],
198 static_cast<int>(rtcp_recv_key.size()),
199 recv_extension_ids)) {
200 RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTCP failed";
201 }
202 }
203
ExtractParams(cricket::DtlsTransportInternal * dtls_transport,int * selected_crypto_suite,rtc::ZeroOnFreeBuffer<unsigned char> * send_key,rtc::ZeroOnFreeBuffer<unsigned char> * recv_key)204 bool DtlsSrtpTransport::ExtractParams(
205 cricket::DtlsTransportInternal* dtls_transport,
206 int* selected_crypto_suite,
207 rtc::ZeroOnFreeBuffer<unsigned char>* send_key,
208 rtc::ZeroOnFreeBuffer<unsigned char>* recv_key) {
209 if (!dtls_transport || !dtls_transport->IsDtlsActive()) {
210 return false;
211 }
212
213 if (!dtls_transport->GetSrtpCryptoSuite(selected_crypto_suite)) {
214 RTC_LOG(LS_ERROR) << "No DTLS-SRTP selected crypto suite";
215 return false;
216 }
217
218 RTC_LOG(LS_INFO) << "Extracting keys from transport: "
219 << dtls_transport->transport_name();
220
221 int key_len;
222 int salt_len;
223 if (!rtc::GetSrtpKeyAndSaltLengths((*selected_crypto_suite), &key_len,
224 &salt_len)) {
225 RTC_LOG(LS_ERROR) << "Unknown DTLS-SRTP crypto suite"
226 << selected_crypto_suite;
227 return false;
228 }
229
230 // OK, we're now doing DTLS (RFC 5764)
231 rtc::ZeroOnFreeBuffer<unsigned char> dtls_buffer(key_len * 2 + salt_len * 2);
232
233 // RFC 5705 exporter using the RFC 5764 parameters
234 if (!dtls_transport->ExportKeyingMaterial(kDtlsSrtpExporterLabel, NULL, 0,
235 false, &dtls_buffer[0],
236 dtls_buffer.size())) {
237 RTC_LOG(LS_WARNING) << "DTLS-SRTP key export failed";
238 RTC_NOTREACHED(); // This should never happen
239 return false;
240 }
241
242 // Sync up the keys with the DTLS-SRTP interface
243 rtc::ZeroOnFreeBuffer<unsigned char> client_write_key(key_len + salt_len);
244 rtc::ZeroOnFreeBuffer<unsigned char> server_write_key(key_len + salt_len);
245 size_t offset = 0;
246 memcpy(&client_write_key[0], &dtls_buffer[offset], key_len);
247 offset += key_len;
248 memcpy(&server_write_key[0], &dtls_buffer[offset], key_len);
249 offset += key_len;
250 memcpy(&client_write_key[key_len], &dtls_buffer[offset], salt_len);
251 offset += salt_len;
252 memcpy(&server_write_key[key_len], &dtls_buffer[offset], salt_len);
253
254 rtc::SSLRole role;
255 if (!dtls_transport->GetDtlsRole(&role)) {
256 RTC_LOG(LS_WARNING) << "Failed to get the DTLS role.";
257 return false;
258 }
259
260 if (role == rtc::SSL_SERVER) {
261 *send_key = std::move(server_write_key);
262 *recv_key = std::move(client_write_key);
263 } else {
264 *send_key = std::move(client_write_key);
265 *recv_key = std::move(server_write_key);
266 }
267 return true;
268 }
269
SetDtlsTransport(cricket::DtlsTransportInternal * new_dtls_transport,cricket::DtlsTransportInternal ** old_dtls_transport)270 void DtlsSrtpTransport::SetDtlsTransport(
271 cricket::DtlsTransportInternal* new_dtls_transport,
272 cricket::DtlsTransportInternal** old_dtls_transport) {
273 if (*old_dtls_transport == new_dtls_transport) {
274 return;
275 }
276
277 if (*old_dtls_transport) {
278 (*old_dtls_transport)->UnsubscribeDtlsState(this);
279 }
280
281 *old_dtls_transport = new_dtls_transport;
282
283 if (new_dtls_transport) {
284 new_dtls_transport->SubscribeDtlsState(
285 this, [this](cricket::DtlsTransportInternal* transport,
286 cricket::DtlsTransportState state) {
287 OnDtlsState(transport, state);
288 });
289 }
290 }
291
SetRtpDtlsTransport(cricket::DtlsTransportInternal * rtp_dtls_transport)292 void DtlsSrtpTransport::SetRtpDtlsTransport(
293 cricket::DtlsTransportInternal* rtp_dtls_transport) {
294 SetDtlsTransport(rtp_dtls_transport, &rtp_dtls_transport_);
295 }
296
SetRtcpDtlsTransport(cricket::DtlsTransportInternal * rtcp_dtls_transport)297 void DtlsSrtpTransport::SetRtcpDtlsTransport(
298 cricket::DtlsTransportInternal* rtcp_dtls_transport) {
299 SetDtlsTransport(rtcp_dtls_transport, &rtcp_dtls_transport_);
300 }
301
OnDtlsState(cricket::DtlsTransportInternal * transport,cricket::DtlsTransportState state)302 void DtlsSrtpTransport::OnDtlsState(cricket::DtlsTransportInternal* transport,
303 cricket::DtlsTransportState state) {
304 RTC_DCHECK(transport == rtp_dtls_transport_ ||
305 transport == rtcp_dtls_transport_);
306
307 if (on_dtls_state_change_) {
308 on_dtls_state_change_();
309 }
310
311 if (state != cricket::DTLS_TRANSPORT_CONNECTED) {
312 ResetParams();
313 return;
314 }
315
316 MaybeSetupDtlsSrtp();
317 }
318
OnWritableState(rtc::PacketTransportInternal * packet_transport)319 void DtlsSrtpTransport::OnWritableState(
320 rtc::PacketTransportInternal* packet_transport) {
321 MaybeSetupDtlsSrtp();
322 }
323
SetOnDtlsStateChange(std::function<void (void)> callback)324 void DtlsSrtpTransport::SetOnDtlsStateChange(
325 std::function<void(void)> callback) {
326 on_dtls_state_change_ = std::move(callback);
327 }
328 } // namespace webrtc
329