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