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/srtp_session.h"
12
13 #include <iomanip>
14
15 #include "absl/base/attributes.h"
16 #include "media/base/rtp_utils.h"
17 #include "pc/external_hmac.h"
18 #include "rtc_base/logging.h"
19 #include "rtc_base/ssl_stream_adapter.h"
20 #include "rtc_base/string_encode.h"
21 #include "rtc_base/time_utils.h"
22 #include "system_wrappers/include/field_trial.h"
23 #include "system_wrappers/include/metrics.h"
24 #include "third_party/libsrtp/include/srtp.h"
25 #include "third_party/libsrtp/include/srtp_priv.h"
26
27 namespace cricket {
28
29 // One more than the maximum libsrtp error code. Required by
30 // RTC_HISTOGRAM_ENUMERATION. Keep this in sync with srtp_error_status_t defined
31 // in srtp.h.
32 constexpr int kSrtpErrorCodeBoundary = 28;
33
SrtpSession()34 SrtpSession::SrtpSession() {
35 dump_plain_rtp_ = webrtc::field_trial::IsEnabled("WebRTC-Debugging-RtpDump");
36 }
37
~SrtpSession()38 SrtpSession::~SrtpSession() {
39 if (session_) {
40 srtp_set_user_data(session_, nullptr);
41 srtp_dealloc(session_);
42 }
43 if (inited_) {
44 DecrementLibsrtpUsageCountAndMaybeDeinit();
45 }
46 }
47
SetSend(int cs,const uint8_t * key,size_t len,const std::vector<int> & extension_ids)48 bool SrtpSession::SetSend(int cs,
49 const uint8_t* key,
50 size_t len,
51 const std::vector<int>& extension_ids) {
52 return SetKey(ssrc_any_outbound, cs, key, len, extension_ids);
53 }
54
UpdateSend(int cs,const uint8_t * key,size_t len,const std::vector<int> & extension_ids)55 bool SrtpSession::UpdateSend(int cs,
56 const uint8_t* key,
57 size_t len,
58 const std::vector<int>& extension_ids) {
59 return UpdateKey(ssrc_any_outbound, cs, key, len, extension_ids);
60 }
61
SetRecv(int cs,const uint8_t * key,size_t len,const std::vector<int> & extension_ids)62 bool SrtpSession::SetRecv(int cs,
63 const uint8_t* key,
64 size_t len,
65 const std::vector<int>& extension_ids) {
66 return SetKey(ssrc_any_inbound, cs, key, len, extension_ids);
67 }
68
UpdateRecv(int cs,const uint8_t * key,size_t len,const std::vector<int> & extension_ids)69 bool SrtpSession::UpdateRecv(int cs,
70 const uint8_t* key,
71 size_t len,
72 const std::vector<int>& extension_ids) {
73 return UpdateKey(ssrc_any_inbound, cs, key, len, extension_ids);
74 }
75
ProtectRtp(void * p,int in_len,int max_len,int * out_len)76 bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
77 RTC_DCHECK(thread_checker_.IsCurrent());
78 if (!session_) {
79 RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet: no SRTP Session";
80 return false;
81 }
82
83 // Note: the need_len differs from the libsrtp recommendatіon to ensure
84 // SRTP_MAX_TRAILER_LEN bytes of free space after the data. WebRTC
85 // never includes a MKI, therefore the amount of bytes added by the
86 // srtp_protect call is known in advance and depends on the cipher suite.
87 int need_len = in_len + rtp_auth_tag_len_; // NOLINT
88 if (max_len < need_len) {
89 RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet: The buffer length "
90 << max_len << " is less than the needed " << need_len;
91 return false;
92 }
93 if (dump_plain_rtp_) {
94 DumpPacket(p, in_len, /*outbound=*/true);
95 }
96
97 *out_len = in_len;
98 int err = srtp_protect(session_, p, out_len);
99 int seq_num;
100 GetRtpSeqNum(p, in_len, &seq_num);
101 if (err != srtp_err_status_ok) {
102 RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet, seqnum=" << seq_num
103 << ", err=" << err
104 << ", last seqnum=" << last_send_seq_num_;
105 return false;
106 }
107 last_send_seq_num_ = seq_num;
108 return true;
109 }
110
ProtectRtp(void * p,int in_len,int max_len,int * out_len,int64_t * index)111 bool SrtpSession::ProtectRtp(void* p,
112 int in_len,
113 int max_len,
114 int* out_len,
115 int64_t* index) {
116 if (!ProtectRtp(p, in_len, max_len, out_len)) {
117 return false;
118 }
119 return (index) ? GetSendStreamPacketIndex(p, in_len, index) : true;
120 }
121
ProtectRtcp(void * p,int in_len,int max_len,int * out_len)122 bool SrtpSession::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) {
123 RTC_DCHECK(thread_checker_.IsCurrent());
124 if (!session_) {
125 RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet: no SRTP Session";
126 return false;
127 }
128
129 // Note: the need_len differs from the libsrtp recommendatіon to ensure
130 // SRTP_MAX_TRAILER_LEN bytes of free space after the data. WebRTC
131 // never includes a MKI, therefore the amount of bytes added by the
132 // srtp_protect_rtp call is known in advance and depends on the cipher suite.
133 int need_len = in_len + sizeof(uint32_t) + rtcp_auth_tag_len_; // NOLINT
134 if (max_len < need_len) {
135 RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet: The buffer length "
136 << max_len << " is less than the needed " << need_len;
137 return false;
138 }
139 if (dump_plain_rtp_) {
140 DumpPacket(p, in_len, /*outbound=*/true);
141 }
142
143 *out_len = in_len;
144 int err = srtp_protect_rtcp(session_, p, out_len);
145 if (err != srtp_err_status_ok) {
146 RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet, err=" << err;
147 return false;
148 }
149 return true;
150 }
151
UnprotectRtp(void * p,int in_len,int * out_len)152 bool SrtpSession::UnprotectRtp(void* p, int in_len, int* out_len) {
153 RTC_DCHECK(thread_checker_.IsCurrent());
154 if (!session_) {
155 RTC_LOG(LS_WARNING) << "Failed to unprotect SRTP packet: no SRTP Session";
156 return false;
157 }
158
159 *out_len = in_len;
160 int err = srtp_unprotect(session_, p, out_len);
161 if (err != srtp_err_status_ok) {
162 // Limit the error logging to avoid excessive logs when there are lots of
163 // bad packets.
164 const int kFailureLogThrottleCount = 100;
165 if (decryption_failure_count_ % kFailureLogThrottleCount == 0) {
166 RTC_LOG(LS_WARNING) << "Failed to unprotect SRTP packet, err=" << err
167 << ", previous failure count: "
168 << decryption_failure_count_;
169 }
170 ++decryption_failure_count_;
171 RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SrtpUnprotectError",
172 static_cast<int>(err), kSrtpErrorCodeBoundary);
173 return false;
174 }
175 if (dump_plain_rtp_) {
176 DumpPacket(p, *out_len, /*outbound=*/false);
177 }
178 return true;
179 }
180
UnprotectRtcp(void * p,int in_len,int * out_len)181 bool SrtpSession::UnprotectRtcp(void* p, int in_len, int* out_len) {
182 RTC_DCHECK(thread_checker_.IsCurrent());
183 if (!session_) {
184 RTC_LOG(LS_WARNING) << "Failed to unprotect SRTCP packet: no SRTP Session";
185 return false;
186 }
187
188 *out_len = in_len;
189 int err = srtp_unprotect_rtcp(session_, p, out_len);
190 if (err != srtp_err_status_ok) {
191 RTC_LOG(LS_WARNING) << "Failed to unprotect SRTCP packet, err=" << err;
192 RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SrtcpUnprotectError",
193 static_cast<int>(err), kSrtpErrorCodeBoundary);
194 return false;
195 }
196 if (dump_plain_rtp_) {
197 DumpPacket(p, *out_len, /*outbound=*/false);
198 }
199 return true;
200 }
201
GetRtpAuthParams(uint8_t ** key,int * key_len,int * tag_len)202 bool SrtpSession::GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len) {
203 RTC_DCHECK(thread_checker_.IsCurrent());
204 RTC_DCHECK(IsExternalAuthActive());
205 if (!IsExternalAuthActive()) {
206 return false;
207 }
208
209 ExternalHmacContext* external_hmac = nullptr;
210 // stream_template will be the reference context for other streams.
211 // Let's use it for getting the keys.
212 srtp_stream_ctx_t* srtp_context = session_->stream_template;
213 if (srtp_context && srtp_context->session_keys &&
214 srtp_context->session_keys->rtp_auth) {
215 external_hmac = reinterpret_cast<ExternalHmacContext*>(
216 srtp_context->session_keys->rtp_auth->state);
217 }
218
219 if (!external_hmac) {
220 RTC_LOG(LS_ERROR) << "Failed to get auth keys from libsrtp!.";
221 return false;
222 }
223
224 *key = external_hmac->key;
225 *key_len = external_hmac->key_length;
226 *tag_len = rtp_auth_tag_len_;
227 return true;
228 }
229
GetSrtpOverhead() const230 int SrtpSession::GetSrtpOverhead() const {
231 return rtp_auth_tag_len_;
232 }
233
EnableExternalAuth()234 void SrtpSession::EnableExternalAuth() {
235 RTC_DCHECK(!session_);
236 external_auth_enabled_ = true;
237 }
238
IsExternalAuthEnabled() const239 bool SrtpSession::IsExternalAuthEnabled() const {
240 return external_auth_enabled_;
241 }
242
IsExternalAuthActive() const243 bool SrtpSession::IsExternalAuthActive() const {
244 return external_auth_active_;
245 }
246
GetSendStreamPacketIndex(void * p,int in_len,int64_t * index)247 bool SrtpSession::GetSendStreamPacketIndex(void* p,
248 int in_len,
249 int64_t* index) {
250 RTC_DCHECK(thread_checker_.IsCurrent());
251 srtp_hdr_t* hdr = reinterpret_cast<srtp_hdr_t*>(p);
252 srtp_stream_ctx_t* stream = srtp_get_stream(session_, hdr->ssrc);
253 if (!stream) {
254 return false;
255 }
256
257 // Shift packet index, put into network byte order
258 *index = static_cast<int64_t>(rtc::NetworkToHost64(
259 srtp_rdbx_get_packet_index(&stream->rtp_rdbx) << 16));
260 return true;
261 }
262
DoSetKey(int type,int cs,const uint8_t * key,size_t len,const std::vector<int> & extension_ids)263 bool SrtpSession::DoSetKey(int type,
264 int cs,
265 const uint8_t* key,
266 size_t len,
267 const std::vector<int>& extension_ids) {
268 RTC_DCHECK(thread_checker_.IsCurrent());
269
270 srtp_policy_t policy;
271 memset(&policy, 0, sizeof(policy));
272 if (!(srtp_crypto_policy_set_from_profile_for_rtp(
273 &policy.rtp, (srtp_profile_t)cs) == srtp_err_status_ok &&
274 srtp_crypto_policy_set_from_profile_for_rtcp(
275 &policy.rtcp, (srtp_profile_t)cs) == srtp_err_status_ok)) {
276 RTC_LOG(LS_ERROR) << "Failed to " << (session_ ? "update" : "create")
277 << " SRTP session: unsupported cipher_suite " << cs;
278 return false;
279 }
280
281 int expected_key_len;
282 int expected_salt_len;
283 if (!rtc::GetSrtpKeyAndSaltLengths(cs, &expected_key_len,
284 &expected_salt_len)) {
285 // This should never happen.
286 RTC_NOTREACHED();
287 RTC_LOG(LS_WARNING)
288 << "Failed to " << (session_ ? "update" : "create")
289 << " SRTP session: unsupported cipher_suite without length information"
290 << cs;
291 return false;
292 }
293
294 if (!key ||
295 len != static_cast<size_t>(expected_key_len + expected_salt_len)) {
296 RTC_LOG(LS_WARNING) << "Failed to " << (session_ ? "update" : "create")
297 << " SRTP session: invalid key";
298 return false;
299 }
300
301 policy.ssrc.type = static_cast<srtp_ssrc_type_t>(type);
302 policy.ssrc.value = 0;
303 policy.key = const_cast<uint8_t*>(key);
304 // TODO(astor) parse window size from WSH session-param
305 policy.window_size = 1024;
306 policy.allow_repeat_tx = 1;
307 // If external authentication option is enabled, supply custom auth module
308 // id EXTERNAL_HMAC_SHA1 in the policy structure.
309 // We want to set this option only for rtp packets.
310 // By default policy structure is initialized to HMAC_SHA1.
311 // Enable external HMAC authentication only for outgoing streams and only
312 // for cipher suites that support it (i.e. only non-GCM cipher suites).
313 if (type == ssrc_any_outbound && IsExternalAuthEnabled() &&
314 !rtc::IsGcmCryptoSuite(cs)) {
315 policy.rtp.auth_type = EXTERNAL_HMAC_SHA1;
316 }
317 if (!extension_ids.empty()) {
318 policy.enc_xtn_hdr = const_cast<int*>(&extension_ids[0]);
319 policy.enc_xtn_hdr_count = static_cast<int>(extension_ids.size());
320 }
321 policy.next = nullptr;
322
323 if (!session_) {
324 int err = srtp_create(&session_, &policy);
325 if (err != srtp_err_status_ok) {
326 session_ = nullptr;
327 RTC_LOG(LS_ERROR) << "Failed to create SRTP session, err=" << err;
328 return false;
329 }
330 srtp_set_user_data(session_, this);
331 } else {
332 int err = srtp_update(session_, &policy);
333 if (err != srtp_err_status_ok) {
334 RTC_LOG(LS_ERROR) << "Failed to update SRTP session, err=" << err;
335 return false;
336 }
337 }
338
339 rtp_auth_tag_len_ = policy.rtp.auth_tag_len;
340 rtcp_auth_tag_len_ = policy.rtcp.auth_tag_len;
341 external_auth_active_ = (policy.rtp.auth_type == EXTERNAL_HMAC_SHA1);
342 return true;
343 }
344
SetKey(int type,int cs,const uint8_t * key,size_t len,const std::vector<int> & extension_ids)345 bool SrtpSession::SetKey(int type,
346 int cs,
347 const uint8_t* key,
348 size_t len,
349 const std::vector<int>& extension_ids) {
350 RTC_DCHECK(thread_checker_.IsCurrent());
351 if (session_) {
352 RTC_LOG(LS_ERROR) << "Failed to create SRTP session: "
353 "SRTP session already created";
354 return false;
355 }
356
357 // This is the first time we need to actually interact with libsrtp, so
358 // initialize it if needed.
359 if (IncrementLibsrtpUsageCountAndMaybeInit()) {
360 inited_ = true;
361 } else {
362 return false;
363 }
364
365 return DoSetKey(type, cs, key, len, extension_ids);
366 }
367
UpdateKey(int type,int cs,const uint8_t * key,size_t len,const std::vector<int> & extension_ids)368 bool SrtpSession::UpdateKey(int type,
369 int cs,
370 const uint8_t* key,
371 size_t len,
372 const std::vector<int>& extension_ids) {
373 RTC_DCHECK(thread_checker_.IsCurrent());
374 if (!session_) {
375 RTC_LOG(LS_ERROR) << "Failed to update non-existing SRTP session";
376 return false;
377 }
378
379 return DoSetKey(type, cs, key, len, extension_ids);
380 }
381
382 ABSL_CONST_INIT int g_libsrtp_usage_count = 0;
383 ABSL_CONST_INIT webrtc::GlobalMutex g_libsrtp_lock(absl::kConstInit);
384
ProhibitLibsrtpInitialization()385 void ProhibitLibsrtpInitialization() {
386 webrtc::GlobalMutexLock ls(&g_libsrtp_lock);
387 ++g_libsrtp_usage_count;
388 }
389
390 // static
IncrementLibsrtpUsageCountAndMaybeInit()391 bool SrtpSession::IncrementLibsrtpUsageCountAndMaybeInit() {
392 webrtc::GlobalMutexLock ls(&g_libsrtp_lock);
393
394 RTC_DCHECK_GE(g_libsrtp_usage_count, 0);
395 if (g_libsrtp_usage_count == 0) {
396 int err;
397 err = srtp_init();
398 if (err != srtp_err_status_ok) {
399 RTC_LOG(LS_ERROR) << "Failed to init SRTP, err=" << err;
400 return false;
401 }
402
403 err = srtp_install_event_handler(&SrtpSession::HandleEventThunk);
404 if (err != srtp_err_status_ok) {
405 RTC_LOG(LS_ERROR) << "Failed to install SRTP event handler, err=" << err;
406 return false;
407 }
408
409 err = external_crypto_init();
410 if (err != srtp_err_status_ok) {
411 RTC_LOG(LS_ERROR) << "Failed to initialize fake auth, err=" << err;
412 return false;
413 }
414 }
415 ++g_libsrtp_usage_count;
416 return true;
417 }
418
419 // static
DecrementLibsrtpUsageCountAndMaybeDeinit()420 void SrtpSession::DecrementLibsrtpUsageCountAndMaybeDeinit() {
421 webrtc::GlobalMutexLock ls(&g_libsrtp_lock);
422
423 RTC_DCHECK_GE(g_libsrtp_usage_count, 1);
424 if (--g_libsrtp_usage_count == 0) {
425 int err = srtp_shutdown();
426 if (err) {
427 RTC_LOG(LS_ERROR) << "srtp_shutdown failed. err=" << err;
428 }
429 }
430 }
431
HandleEvent(const srtp_event_data_t * ev)432 void SrtpSession::HandleEvent(const srtp_event_data_t* ev) {
433 RTC_DCHECK(thread_checker_.IsCurrent());
434 switch (ev->event) {
435 case event_ssrc_collision:
436 RTC_LOG(LS_INFO) << "SRTP event: SSRC collision";
437 break;
438 case event_key_soft_limit:
439 RTC_LOG(LS_INFO) << "SRTP event: reached soft key usage limit";
440 break;
441 case event_key_hard_limit:
442 RTC_LOG(LS_INFO) << "SRTP event: reached hard key usage limit";
443 break;
444 case event_packet_index_limit:
445 RTC_LOG(LS_INFO)
446 << "SRTP event: reached hard packet limit (2^48 packets)";
447 break;
448 default:
449 RTC_LOG(LS_INFO) << "SRTP event: unknown " << ev->event;
450 break;
451 }
452 }
453
HandleEventThunk(srtp_event_data_t * ev)454 void SrtpSession::HandleEventThunk(srtp_event_data_t* ev) {
455 // Callback will be executed from same thread that calls the "srtp_protect"
456 // and "srtp_unprotect" functions.
457 SrtpSession* session =
458 static_cast<SrtpSession*>(srtp_get_user_data(ev->session));
459 if (session) {
460 session->HandleEvent(ev);
461 }
462 }
463
464 // Logs the unencrypted packet in text2pcap format. This can then be
465 // extracted by searching for RTP_DUMP
466 // grep RTP_DUMP chrome_debug.log > in.txt
467 // and converted to pcap using
468 // text2pcap -D -u 1000,2000 -t %H:%M:%S. in.txt out.pcap
469 // The resulting file can be replayed using the WebRTC video_replay tool and
470 // be inspected in Wireshark using the RTP, VP8 and H264 dissectors.
DumpPacket(const void * buf,int len,bool outbound)471 void SrtpSession::DumpPacket(const void* buf, int len, bool outbound) {
472 int64_t time_of_day = rtc::TimeUTCMillis() % (24 * 3600 * 1000);
473 int64_t hours = time_of_day / (3600 * 1000);
474 int64_t minutes = (time_of_day / (60 * 1000)) % 60;
475 int64_t seconds = (time_of_day / 1000) % 60;
476 int64_t millis = time_of_day % 1000;
477 RTC_LOG(LS_VERBOSE) << "\n" << (outbound ? "O" : "I") << " "
478 << std::setfill('0') << std::setw(2) << hours << ":"
479 << std::setfill('0') << std::setw(2) << minutes << ":"
480 << std::setfill('0') << std::setw(2) << seconds << "."
481 << std::setfill('0') << std::setw(3) << millis << " "
482 << "000000 " << rtc::hex_encode_with_delimiter((const char *)buf, len, ' ')
483 << " # RTP_DUMP";
484 }
485
486 } // namespace cricket
487