blob: 9d67669dd0c36b3839140658b29198cf0d73edef [file] [log] [blame]
zstein4dde3df2017-07-07 21:26:251/*
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
Steve Anton10542f22019-01-11 17:11:0011#include "pc/srtp_session.h"
zstein4dde3df2017-07-07 21:26:2512
Steve Anton10542f22019-01-11 17:11:0013#include "media/base/rtp_utils.h"
14#include "pc/external_hmac.h"
15#include "rtc_base/critical_section.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3116#include "rtc_base/logging.h"
Steve Anton10542f22019-01-11 17:11:0017#include "rtc_base/ssl_stream_adapter.h"
Zhi Huang0a5fdbb2018-06-14 17:40:1918#include "system_wrappers/include/metrics.h"
Taylor Brandstetterb140b9f2017-10-13 00:24:1619#include "third_party/libsrtp/include/srtp.h"
20#include "third_party/libsrtp/include/srtp_priv.h"
zstein4dde3df2017-07-07 21:26:2521
22namespace cricket {
23
Zhi Huang0a5fdbb2018-06-14 17:40:1924// One more than the maximum libsrtp error code. Required by
25// RTC_HISTOGRAM_ENUMERATION. Keep this in sync with srtp_error_status_t defined
26// in srtp.h.
27constexpr int kSrtpErrorCodeBoundary = 28;
28
zstein4dde3df2017-07-07 21:26:2529SrtpSession::SrtpSession() {}
30
31SrtpSession::~SrtpSession() {
32 if (session_) {
33 srtp_set_user_data(session_, nullptr);
34 srtp_dealloc(session_);
35 }
Taylor Brandstetterb140b9f2017-10-13 00:24:1636 if (inited_) {
37 DecrementLibsrtpUsageCountAndMaybeDeinit();
38 }
zstein4dde3df2017-07-07 21:26:2539}
40
Zhi Huangc99b6c72017-11-11 00:44:4641bool SrtpSession::SetSend(int cs,
42 const uint8_t* key,
43 size_t len,
44 const std::vector<int>& extension_ids) {
45 return SetKey(ssrc_any_outbound, cs, key, len, extension_ids);
zstein4dde3df2017-07-07 21:26:2546}
47
Zhi Huangc99b6c72017-11-11 00:44:4648bool SrtpSession::UpdateSend(int cs,
49 const uint8_t* key,
50 size_t len,
51 const std::vector<int>& extension_ids) {
52 return UpdateKey(ssrc_any_outbound, cs, key, len, extension_ids);
zstein4dde3df2017-07-07 21:26:2553}
54
Zhi Huangc99b6c72017-11-11 00:44:4655bool SrtpSession::SetRecv(int cs,
56 const uint8_t* key,
57 size_t len,
58 const std::vector<int>& extension_ids) {
59 return SetKey(ssrc_any_inbound, cs, key, len, extension_ids);
zstein4dde3df2017-07-07 21:26:2560}
61
Zhi Huangc99b6c72017-11-11 00:44:4662bool SrtpSession::UpdateRecv(int cs,
63 const uint8_t* key,
64 size_t len,
65 const std::vector<int>& extension_ids) {
66 return UpdateKey(ssrc_any_inbound, cs, key, len, extension_ids);
zstein4dde3df2017-07-07 21:26:2567}
68
69bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
Sebastian Janssonc01367d2019-04-08 13:20:4470 RTC_DCHECK(thread_checker_.IsCurrent());
zstein4dde3df2017-07-07 21:26:2571 if (!session_) {
Mirko Bonadei675513b2017-11-09 10:09:2572 RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet: no SRTP Session";
zstein4dde3df2017-07-07 21:26:2573 return false;
74 }
75
76 int need_len = in_len + rtp_auth_tag_len_; // NOLINT
77 if (max_len < need_len) {
Mirko Bonadei675513b2017-11-09 10:09:2578 RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet: The buffer length "
79 << max_len << " is less than the needed " << need_len;
zstein4dde3df2017-07-07 21:26:2580 return false;
81 }
82
83 *out_len = in_len;
84 int err = srtp_protect(session_, p, out_len);
85 int seq_num;
86 GetRtpSeqNum(p, in_len, &seq_num);
87 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 10:09:2588 RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet, seqnum=" << seq_num
89 << ", err=" << err
90 << ", last seqnum=" << last_send_seq_num_;
zstein4dde3df2017-07-07 21:26:2591 return false;
92 }
93 last_send_seq_num_ = seq_num;
94 return true;
95}
96
97bool SrtpSession::ProtectRtp(void* p,
98 int in_len,
99 int max_len,
100 int* out_len,
101 int64_t* index) {
102 if (!ProtectRtp(p, in_len, max_len, out_len)) {
103 return false;
104 }
105 return (index) ? GetSendStreamPacketIndex(p, in_len, index) : true;
106}
107
108bool SrtpSession::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) {
Sebastian Janssonc01367d2019-04-08 13:20:44109 RTC_DCHECK(thread_checker_.IsCurrent());
zstein4dde3df2017-07-07 21:26:25110 if (!session_) {
Mirko Bonadei675513b2017-11-09 10:09:25111 RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet: no SRTP Session";
zstein4dde3df2017-07-07 21:26:25112 return false;
113 }
114
115 int need_len = in_len + sizeof(uint32_t) + rtcp_auth_tag_len_; // NOLINT
116 if (max_len < need_len) {
Mirko Bonadei675513b2017-11-09 10:09:25117 RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet: The buffer length "
118 << max_len << " is less than the needed " << need_len;
zstein4dde3df2017-07-07 21:26:25119 return false;
120 }
121
122 *out_len = in_len;
123 int err = srtp_protect_rtcp(session_, p, out_len);
124 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 10:09:25125 RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet, err=" << err;
zstein4dde3df2017-07-07 21:26:25126 return false;
127 }
128 return true;
129}
130
131bool SrtpSession::UnprotectRtp(void* p, int in_len, int* out_len) {
Sebastian Janssonc01367d2019-04-08 13:20:44132 RTC_DCHECK(thread_checker_.IsCurrent());
zstein4dde3df2017-07-07 21:26:25133 if (!session_) {
Mirko Bonadei675513b2017-11-09 10:09:25134 RTC_LOG(LS_WARNING) << "Failed to unprotect SRTP packet: no SRTP Session";
zstein4dde3df2017-07-07 21:26:25135 return false;
136 }
137
138 *out_len = in_len;
139 int err = srtp_unprotect(session_, p, out_len);
140 if (err != srtp_err_status_ok) {
erikvarga@webrtc.orgd76a0fc2018-10-09 10:31:28141 // Limit the error logging to avoid excessive logs when there are lots of
142 // bad packets.
143 const int kFailureLogThrottleCount = 100;
144 if (decryption_failure_count_ % kFailureLogThrottleCount == 0) {
145 RTC_LOG(LS_WARNING) << "Failed to unprotect SRTP packet, err=" << err
146 << ", previous failure count: "
147 << decryption_failure_count_;
148 }
149 ++decryption_failure_count_;
Qingsi Wang7fc821d2018-07-12 19:54:53150 RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SrtpUnprotectError",
Zhi Huang0a5fdbb2018-06-14 17:40:19151 static_cast<int>(err), kSrtpErrorCodeBoundary);
zstein4dde3df2017-07-07 21:26:25152 return false;
153 }
154 return true;
155}
156
157bool SrtpSession::UnprotectRtcp(void* p, int in_len, int* out_len) {
Sebastian Janssonc01367d2019-04-08 13:20:44158 RTC_DCHECK(thread_checker_.IsCurrent());
zstein4dde3df2017-07-07 21:26:25159 if (!session_) {
Mirko Bonadei675513b2017-11-09 10:09:25160 RTC_LOG(LS_WARNING) << "Failed to unprotect SRTCP packet: no SRTP Session";
zstein4dde3df2017-07-07 21:26:25161 return false;
162 }
163
164 *out_len = in_len;
165 int err = srtp_unprotect_rtcp(session_, p, out_len);
166 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 10:09:25167 RTC_LOG(LS_WARNING) << "Failed to unprotect SRTCP packet, err=" << err;
Qingsi Wang7fc821d2018-07-12 19:54:53168 RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SrtcpUnprotectError",
Zhi Huang0a5fdbb2018-06-14 17:40:19169 static_cast<int>(err), kSrtpErrorCodeBoundary);
zstein4dde3df2017-07-07 21:26:25170 return false;
171 }
172 return true;
173}
174
175bool SrtpSession::GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len) {
Sebastian Janssonc01367d2019-04-08 13:20:44176 RTC_DCHECK(thread_checker_.IsCurrent());
zstein4dde3df2017-07-07 21:26:25177 RTC_DCHECK(IsExternalAuthActive());
178 if (!IsExternalAuthActive()) {
179 return false;
180 }
181
182 ExternalHmacContext* external_hmac = nullptr;
183 // stream_template will be the reference context for other streams.
184 // Let's use it for getting the keys.
185 srtp_stream_ctx_t* srtp_context = session_->stream_template;
zstein4dde3df2017-07-07 21:26:25186 if (srtp_context && srtp_context->session_keys &&
187 srtp_context->session_keys->rtp_auth) {
188 external_hmac = reinterpret_cast<ExternalHmacContext*>(
189 srtp_context->session_keys->rtp_auth->state);
190 }
zstein4dde3df2017-07-07 21:26:25191
192 if (!external_hmac) {
Mirko Bonadei675513b2017-11-09 10:09:25193 RTC_LOG(LS_ERROR) << "Failed to get auth keys from libsrtp!.";
zstein4dde3df2017-07-07 21:26:25194 return false;
195 }
196
197 *key = external_hmac->key;
198 *key_len = external_hmac->key_length;
199 *tag_len = rtp_auth_tag_len_;
200 return true;
201}
202
203int SrtpSession::GetSrtpOverhead() const {
204 return rtp_auth_tag_len_;
205}
206
207void SrtpSession::EnableExternalAuth() {
208 RTC_DCHECK(!session_);
209 external_auth_enabled_ = true;
210}
211
212bool SrtpSession::IsExternalAuthEnabled() const {
213 return external_auth_enabled_;
214}
215
216bool SrtpSession::IsExternalAuthActive() const {
217 return external_auth_active_;
218}
219
220bool SrtpSession::GetSendStreamPacketIndex(void* p,
221 int in_len,
222 int64_t* index) {
Sebastian Janssonc01367d2019-04-08 13:20:44223 RTC_DCHECK(thread_checker_.IsCurrent());
zstein4dde3df2017-07-07 21:26:25224 srtp_hdr_t* hdr = reinterpret_cast<srtp_hdr_t*>(p);
225 srtp_stream_ctx_t* stream = srtp_get_stream(session_, hdr->ssrc);
226 if (!stream) {
227 return false;
228 }
229
230 // Shift packet index, put into network byte order
231 *index = static_cast<int64_t>(rtc::NetworkToHost64(
232 srtp_rdbx_get_packet_index(&stream->rtp_rdbx) << 16));
233 return true;
234}
235
Zhi Huangc99b6c72017-11-11 00:44:46236bool SrtpSession::DoSetKey(int type,
237 int cs,
238 const uint8_t* key,
239 size_t len,
240 const std::vector<int>& extension_ids) {
Sebastian Janssonc01367d2019-04-08 13:20:44241 RTC_DCHECK(thread_checker_.IsCurrent());
zstein4dde3df2017-07-07 21:26:25242
243 srtp_policy_t policy;
244 memset(&policy, 0, sizeof(policy));
245 if (cs == rtc::SRTP_AES128_CM_SHA1_80) {
246 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
247 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
248 } else if (cs == rtc::SRTP_AES128_CM_SHA1_32) {
249 // RTP HMAC is shortened to 32 bits, but RTCP remains 80 bits.
250 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp);
251 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
252 } else if (cs == rtc::SRTP_AEAD_AES_128_GCM) {
253 srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtp);
254 srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtcp);
255 } else if (cs == rtc::SRTP_AEAD_AES_256_GCM) {
256 srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp);
257 srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtcp);
258 } else {
Mirko Bonadei675513b2017-11-09 10:09:25259 RTC_LOG(LS_WARNING) << "Failed to " << (session_ ? "update" : "create")
260 << " SRTP session: unsupported cipher_suite " << cs;
zstein4dde3df2017-07-07 21:26:25261 return false;
262 }
263
264 int expected_key_len;
265 int expected_salt_len;
266 if (!rtc::GetSrtpKeyAndSaltLengths(cs, &expected_key_len,
267 &expected_salt_len)) {
268 // This should never happen.
Jonas Olsson45cc8902018-02-13 09:37:07269 RTC_NOTREACHED();
Mirko Bonadei675513b2017-11-09 10:09:25270 RTC_LOG(LS_WARNING)
zstein4dde3df2017-07-07 21:26:25271 << "Failed to " << (session_ ? "update" : "create")
272 << " SRTP session: unsupported cipher_suite without length information"
273 << cs;
274 return false;
275 }
276
277 if (!key ||
278 len != static_cast<size_t>(expected_key_len + expected_salt_len)) {
Mirko Bonadei675513b2017-11-09 10:09:25279 RTC_LOG(LS_WARNING) << "Failed to " << (session_ ? "update" : "create")
280 << " SRTP session: invalid key";
zstein4dde3df2017-07-07 21:26:25281 return false;
282 }
283
284 policy.ssrc.type = static_cast<srtp_ssrc_type_t>(type);
285 policy.ssrc.value = 0;
286 policy.key = const_cast<uint8_t*>(key);
287 // TODO(astor) parse window size from WSH session-param
288 policy.window_size = 1024;
289 policy.allow_repeat_tx = 1;
290 // If external authentication option is enabled, supply custom auth module
291 // id EXTERNAL_HMAC_SHA1 in the policy structure.
292 // We want to set this option only for rtp packets.
293 // By default policy structure is initialized to HMAC_SHA1.
294 // Enable external HMAC authentication only for outgoing streams and only
295 // for cipher suites that support it (i.e. only non-GCM cipher suites).
296 if (type == ssrc_any_outbound && IsExternalAuthEnabled() &&
297 !rtc::IsGcmCryptoSuite(cs)) {
298 policy.rtp.auth_type = EXTERNAL_HMAC_SHA1;
299 }
Zhi Huangc99b6c72017-11-11 00:44:46300 if (!extension_ids.empty()) {
301 policy.enc_xtn_hdr = const_cast<int*>(&extension_ids[0]);
302 policy.enc_xtn_hdr_count = static_cast<int>(extension_ids.size());
zstein4dde3df2017-07-07 21:26:25303 }
304 policy.next = nullptr;
305
306 if (!session_) {
307 int err = srtp_create(&session_, &policy);
308 if (err != srtp_err_status_ok) {
309 session_ = nullptr;
Mirko Bonadei675513b2017-11-09 10:09:25310 RTC_LOG(LS_ERROR) << "Failed to create SRTP session, err=" << err;
zstein4dde3df2017-07-07 21:26:25311 return false;
312 }
313 srtp_set_user_data(session_, this);
314 } else {
315 int err = srtp_update(session_, &policy);
316 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 10:09:25317 RTC_LOG(LS_ERROR) << "Failed to update SRTP session, err=" << err;
zstein4dde3df2017-07-07 21:26:25318 return false;
319 }
320 }
321
322 rtp_auth_tag_len_ = policy.rtp.auth_tag_len;
323 rtcp_auth_tag_len_ = policy.rtcp.auth_tag_len;
324 external_auth_active_ = (policy.rtp.auth_type == EXTERNAL_HMAC_SHA1);
325 return true;
326}
327
Zhi Huangc99b6c72017-11-11 00:44:46328bool SrtpSession::SetKey(int type,
329 int cs,
330 const uint8_t* key,
331 size_t len,
332 const std::vector<int>& extension_ids) {
Sebastian Janssonc01367d2019-04-08 13:20:44333 RTC_DCHECK(thread_checker_.IsCurrent());
zstein4dde3df2017-07-07 21:26:25334 if (session_) {
Mirko Bonadei675513b2017-11-09 10:09:25335 RTC_LOG(LS_ERROR) << "Failed to create SRTP session: "
Jonas Olsson45cc8902018-02-13 09:37:07336 "SRTP session already created";
zstein4dde3df2017-07-07 21:26:25337 return false;
338 }
339
Taylor Brandstetterb140b9f2017-10-13 00:24:16340 // This is the first time we need to actually interact with libsrtp, so
341 // initialize it if needed.
342 if (IncrementLibsrtpUsageCountAndMaybeInit()) {
343 inited_ = true;
344 } else {
zstein4dde3df2017-07-07 21:26:25345 return false;
346 }
347
Zhi Huangc99b6c72017-11-11 00:44:46348 return DoSetKey(type, cs, key, len, extension_ids);
zstein4dde3df2017-07-07 21:26:25349}
350
Zhi Huangc99b6c72017-11-11 00:44:46351bool SrtpSession::UpdateKey(int type,
352 int cs,
353 const uint8_t* key,
354 size_t len,
355 const std::vector<int>& extension_ids) {
Sebastian Janssonc01367d2019-04-08 13:20:44356 RTC_DCHECK(thread_checker_.IsCurrent());
zstein4dde3df2017-07-07 21:26:25357 if (!session_) {
Mirko Bonadei675513b2017-11-09 10:09:25358 RTC_LOG(LS_ERROR) << "Failed to update non-existing SRTP session";
zstein4dde3df2017-07-07 21:26:25359 return false;
360 }
361
Zhi Huangc99b6c72017-11-11 00:44:46362 return DoSetKey(type, cs, key, len, extension_ids);
zstein4dde3df2017-07-07 21:26:25363}
364
Taylor Brandstetterb140b9f2017-10-13 00:24:16365int g_libsrtp_usage_count = 0;
366rtc::GlobalLockPod g_libsrtp_lock;
zstein4dde3df2017-07-07 21:26:25367
Taylor Brandstetterb140b9f2017-10-13 00:24:16368// static
369bool SrtpSession::IncrementLibsrtpUsageCountAndMaybeInit() {
370 rtc::GlobalLockScope ls(&g_libsrtp_lock);
371
372 RTC_DCHECK_GE(g_libsrtp_usage_count, 0);
373 if (g_libsrtp_usage_count == 0) {
zstein4dde3df2017-07-07 21:26:25374 int err;
375 err = srtp_init();
376 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 10:09:25377 RTC_LOG(LS_ERROR) << "Failed to init SRTP, err=" << err;
zstein4dde3df2017-07-07 21:26:25378 return false;
379 }
380
381 err = srtp_install_event_handler(&SrtpSession::HandleEventThunk);
382 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 10:09:25383 RTC_LOG(LS_ERROR) << "Failed to install SRTP event handler, err=" << err;
zstein4dde3df2017-07-07 21:26:25384 return false;
385 }
386
387 err = external_crypto_init();
388 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 10:09:25389 RTC_LOG(LS_ERROR) << "Failed to initialize fake auth, err=" << err;
zstein4dde3df2017-07-07 21:26:25390 return false;
391 }
zstein4dde3df2017-07-07 21:26:25392 }
Taylor Brandstetterb140b9f2017-10-13 00:24:16393 ++g_libsrtp_usage_count;
zstein4dde3df2017-07-07 21:26:25394 return true;
395}
396
Taylor Brandstetterb140b9f2017-10-13 00:24:16397// static
398void SrtpSession::DecrementLibsrtpUsageCountAndMaybeDeinit() {
399 rtc::GlobalLockScope ls(&g_libsrtp_lock);
zstein4dde3df2017-07-07 21:26:25400
Taylor Brandstetterb140b9f2017-10-13 00:24:16401 RTC_DCHECK_GE(g_libsrtp_usage_count, 1);
402 if (--g_libsrtp_usage_count == 0) {
zstein4dde3df2017-07-07 21:26:25403 int err = srtp_shutdown();
404 if (err) {
Mirko Bonadei675513b2017-11-09 10:09:25405 RTC_LOG(LS_ERROR) << "srtp_shutdown failed. err=" << err;
zstein4dde3df2017-07-07 21:26:25406 }
zstein4dde3df2017-07-07 21:26:25407 }
408}
409
410void SrtpSession::HandleEvent(const srtp_event_data_t* ev) {
Sebastian Janssonc01367d2019-04-08 13:20:44411 RTC_DCHECK(thread_checker_.IsCurrent());
zstein4dde3df2017-07-07 21:26:25412 switch (ev->event) {
413 case event_ssrc_collision:
Mirko Bonadei675513b2017-11-09 10:09:25414 RTC_LOG(LS_INFO) << "SRTP event: SSRC collision";
zstein4dde3df2017-07-07 21:26:25415 break;
416 case event_key_soft_limit:
Mirko Bonadei675513b2017-11-09 10:09:25417 RTC_LOG(LS_INFO) << "SRTP event: reached soft key usage limit";
zstein4dde3df2017-07-07 21:26:25418 break;
419 case event_key_hard_limit:
Mirko Bonadei675513b2017-11-09 10:09:25420 RTC_LOG(LS_INFO) << "SRTP event: reached hard key usage limit";
zstein4dde3df2017-07-07 21:26:25421 break;
422 case event_packet_index_limit:
Mirko Bonadei675513b2017-11-09 10:09:25423 RTC_LOG(LS_INFO)
424 << "SRTP event: reached hard packet limit (2^48 packets)";
zstein4dde3df2017-07-07 21:26:25425 break;
426 default:
Mirko Bonadei675513b2017-11-09 10:09:25427 RTC_LOG(LS_INFO) << "SRTP event: unknown " << ev->event;
zstein4dde3df2017-07-07 21:26:25428 break;
429 }
430}
431
432void SrtpSession::HandleEventThunk(srtp_event_data_t* ev) {
433 // Callback will be executed from same thread that calls the "srtp_protect"
434 // and "srtp_unprotect" functions.
435 SrtpSession* session =
436 static_cast<SrtpSession*>(srtp_get_user_data(ev->session));
437 if (session) {
438 session->HandleEvent(ev);
439 }
440}
441
442} // namespace cricket