blob: c0d05b74f4c9d78b4da1f60b1752d889a22c1058 [file] [log] [blame]
zstein4dde3df2017-07-07 14:26:25 -07001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "pc/srtpsession.h"
zstein4dde3df2017-07-07 14:26:25 -070012
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020013#include "media/base/rtputils.h"
14#include "pc/externalhmac.h"
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -070015#include "rtc_base/criticalsection.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "rtc_base/logging.h"
17#include "rtc_base/sslstreamadapter.h"
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -070018#include "third_party/libsrtp/include/srtp.h"
19#include "third_party/libsrtp/include/srtp_priv.h"
zstein4dde3df2017-07-07 14:26:25 -070020
21namespace cricket {
22
zstein4dde3df2017-07-07 14:26:25 -070023SrtpSession::SrtpSession() {}
24
25SrtpSession::~SrtpSession() {
26 if (session_) {
27 srtp_set_user_data(session_, nullptr);
28 srtp_dealloc(session_);
29 }
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -070030 if (inited_) {
31 DecrementLibsrtpUsageCountAndMaybeDeinit();
32 }
zstein4dde3df2017-07-07 14:26:25 -070033}
34
Zhi Huangc99b6c72017-11-10 16:44:46 -080035bool SrtpSession::SetSend(int cs,
36 const uint8_t* key,
37 size_t len,
38 const std::vector<int>& extension_ids) {
39 return SetKey(ssrc_any_outbound, cs, key, len, extension_ids);
zstein4dde3df2017-07-07 14:26:25 -070040}
41
Zhi Huangc99b6c72017-11-10 16:44:46 -080042bool SrtpSession::UpdateSend(int cs,
43 const uint8_t* key,
44 size_t len,
45 const std::vector<int>& extension_ids) {
46 return UpdateKey(ssrc_any_outbound, cs, key, len, extension_ids);
zstein4dde3df2017-07-07 14:26:25 -070047}
48
Zhi Huangc99b6c72017-11-10 16:44:46 -080049bool SrtpSession::SetRecv(int cs,
50 const uint8_t* key,
51 size_t len,
52 const std::vector<int>& extension_ids) {
53 return SetKey(ssrc_any_inbound, cs, key, len, extension_ids);
zstein4dde3df2017-07-07 14:26:25 -070054}
55
Zhi Huangc99b6c72017-11-10 16:44:46 -080056bool SrtpSession::UpdateRecv(int cs,
57 const uint8_t* key,
58 size_t len,
59 const std::vector<int>& extension_ids) {
60 return UpdateKey(ssrc_any_inbound, cs, key, len, extension_ids);
zstein4dde3df2017-07-07 14:26:25 -070061}
62
63bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
64 RTC_DCHECK(thread_checker_.CalledOnValidThread());
65 if (!session_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010066 RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet: no SRTP Session";
zstein4dde3df2017-07-07 14:26:25 -070067 return false;
68 }
69
70 int need_len = in_len + rtp_auth_tag_len_; // NOLINT
71 if (max_len < need_len) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010072 RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet: The buffer length "
73 << max_len << " is less than the needed " << need_len;
zstein4dde3df2017-07-07 14:26:25 -070074 return false;
75 }
76
77 *out_len = in_len;
78 int err = srtp_protect(session_, p, out_len);
79 int seq_num;
80 GetRtpSeqNum(p, in_len, &seq_num);
81 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010082 RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet, seqnum=" << seq_num
83 << ", err=" << err
84 << ", last seqnum=" << last_send_seq_num_;
zstein4dde3df2017-07-07 14:26:25 -070085 return false;
86 }
87 last_send_seq_num_ = seq_num;
88 return true;
89}
90
91bool SrtpSession::ProtectRtp(void* p,
92 int in_len,
93 int max_len,
94 int* out_len,
95 int64_t* index) {
96 if (!ProtectRtp(p, in_len, max_len, out_len)) {
97 return false;
98 }
99 return (index) ? GetSendStreamPacketIndex(p, in_len, index) : true;
100}
101
102bool SrtpSession::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) {
103 RTC_DCHECK(thread_checker_.CalledOnValidThread());
104 if (!session_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100105 RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet: no SRTP Session";
zstein4dde3df2017-07-07 14:26:25 -0700106 return false;
107 }
108
109 int need_len = in_len + sizeof(uint32_t) + rtcp_auth_tag_len_; // NOLINT
110 if (max_len < need_len) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100111 RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet: The buffer length "
112 << max_len << " is less than the needed " << need_len;
zstein4dde3df2017-07-07 14:26:25 -0700113 return false;
114 }
115
116 *out_len = in_len;
117 int err = srtp_protect_rtcp(session_, p, out_len);
118 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100119 RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet, err=" << err;
zstein4dde3df2017-07-07 14:26:25 -0700120 return false;
121 }
122 return true;
123}
124
125bool SrtpSession::UnprotectRtp(void* p, int in_len, int* out_len) {
126 RTC_DCHECK(thread_checker_.CalledOnValidThread());
127 if (!session_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100128 RTC_LOG(LS_WARNING) << "Failed to unprotect SRTP packet: no SRTP Session";
zstein4dde3df2017-07-07 14:26:25 -0700129 return false;
130 }
131
132 *out_len = in_len;
133 int err = srtp_unprotect(session_, p, out_len);
134 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100135 RTC_LOG(LS_WARNING) << "Failed to unprotect SRTP packet, err=" << err;
Steve Antondb67ba12018-03-19 17:41:42 -0700136 if (metrics_observer_) {
137 metrics_observer_->IncrementSparseEnumCounter(
138 webrtc::kEnumCounterSrtpUnprotectError, err);
139 }
zstein4dde3df2017-07-07 14:26:25 -0700140 return false;
141 }
142 return true;
143}
144
145bool SrtpSession::UnprotectRtcp(void* p, int in_len, int* out_len) {
146 RTC_DCHECK(thread_checker_.CalledOnValidThread());
147 if (!session_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100148 RTC_LOG(LS_WARNING) << "Failed to unprotect SRTCP packet: no SRTP Session";
zstein4dde3df2017-07-07 14:26:25 -0700149 return false;
150 }
151
152 *out_len = in_len;
153 int err = srtp_unprotect_rtcp(session_, p, out_len);
154 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100155 RTC_LOG(LS_WARNING) << "Failed to unprotect SRTCP packet, err=" << err;
Steve Antondb67ba12018-03-19 17:41:42 -0700156 if (metrics_observer_) {
157 metrics_observer_->IncrementSparseEnumCounter(
158 webrtc::kEnumCounterSrtcpUnprotectError, err);
159 }
zstein4dde3df2017-07-07 14:26:25 -0700160 return false;
161 }
162 return true;
163}
164
165bool SrtpSession::GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len) {
166 RTC_DCHECK(thread_checker_.CalledOnValidThread());
167 RTC_DCHECK(IsExternalAuthActive());
168 if (!IsExternalAuthActive()) {
169 return false;
170 }
171
172 ExternalHmacContext* external_hmac = nullptr;
173 // stream_template will be the reference context for other streams.
174 // Let's use it for getting the keys.
175 srtp_stream_ctx_t* srtp_context = session_->stream_template;
zstein4dde3df2017-07-07 14:26:25 -0700176 if (srtp_context && srtp_context->session_keys &&
177 srtp_context->session_keys->rtp_auth) {
178 external_hmac = reinterpret_cast<ExternalHmacContext*>(
179 srtp_context->session_keys->rtp_auth->state);
180 }
zstein4dde3df2017-07-07 14:26:25 -0700181
182 if (!external_hmac) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100183 RTC_LOG(LS_ERROR) << "Failed to get auth keys from libsrtp!.";
zstein4dde3df2017-07-07 14:26:25 -0700184 return false;
185 }
186
187 *key = external_hmac->key;
188 *key_len = external_hmac->key_length;
189 *tag_len = rtp_auth_tag_len_;
190 return true;
191}
192
193int SrtpSession::GetSrtpOverhead() const {
194 return rtp_auth_tag_len_;
195}
196
197void SrtpSession::EnableExternalAuth() {
198 RTC_DCHECK(!session_);
199 external_auth_enabled_ = true;
200}
201
202bool SrtpSession::IsExternalAuthEnabled() const {
203 return external_auth_enabled_;
204}
205
206bool SrtpSession::IsExternalAuthActive() const {
207 return external_auth_active_;
208}
209
210bool SrtpSession::GetSendStreamPacketIndex(void* p,
211 int in_len,
212 int64_t* index) {
213 RTC_DCHECK(thread_checker_.CalledOnValidThread());
214 srtp_hdr_t* hdr = reinterpret_cast<srtp_hdr_t*>(p);
215 srtp_stream_ctx_t* stream = srtp_get_stream(session_, hdr->ssrc);
216 if (!stream) {
217 return false;
218 }
219
220 // Shift packet index, put into network byte order
221 *index = static_cast<int64_t>(rtc::NetworkToHost64(
222 srtp_rdbx_get_packet_index(&stream->rtp_rdbx) << 16));
223 return true;
224}
225
Zhi Huangc99b6c72017-11-10 16:44:46 -0800226bool SrtpSession::DoSetKey(int type,
227 int cs,
228 const uint8_t* key,
229 size_t len,
230 const std::vector<int>& extension_ids) {
zstein4dde3df2017-07-07 14:26:25 -0700231 RTC_DCHECK(thread_checker_.CalledOnValidThread());
232
233 srtp_policy_t policy;
234 memset(&policy, 0, sizeof(policy));
235 if (cs == rtc::SRTP_AES128_CM_SHA1_80) {
236 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
237 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
238 } else if (cs == rtc::SRTP_AES128_CM_SHA1_32) {
239 // RTP HMAC is shortened to 32 bits, but RTCP remains 80 bits.
240 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp);
241 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
242 } else if (cs == rtc::SRTP_AEAD_AES_128_GCM) {
243 srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtp);
244 srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtcp);
245 } else if (cs == rtc::SRTP_AEAD_AES_256_GCM) {
246 srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp);
247 srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtcp);
248 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100249 RTC_LOG(LS_WARNING) << "Failed to " << (session_ ? "update" : "create")
250 << " SRTP session: unsupported cipher_suite " << cs;
zstein4dde3df2017-07-07 14:26:25 -0700251 return false;
252 }
253
254 int expected_key_len;
255 int expected_salt_len;
256 if (!rtc::GetSrtpKeyAndSaltLengths(cs, &expected_key_len,
257 &expected_salt_len)) {
258 // This should never happen.
Jonas Olsson45cc8902018-02-13 10:37:07 +0100259 RTC_NOTREACHED();
Mirko Bonadei675513b2017-11-09 11:09:25 +0100260 RTC_LOG(LS_WARNING)
zstein4dde3df2017-07-07 14:26:25 -0700261 << "Failed to " << (session_ ? "update" : "create")
262 << " SRTP session: unsupported cipher_suite without length information"
263 << cs;
264 return false;
265 }
266
267 if (!key ||
268 len != static_cast<size_t>(expected_key_len + expected_salt_len)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100269 RTC_LOG(LS_WARNING) << "Failed to " << (session_ ? "update" : "create")
270 << " SRTP session: invalid key";
zstein4dde3df2017-07-07 14:26:25 -0700271 return false;
272 }
273
274 policy.ssrc.type = static_cast<srtp_ssrc_type_t>(type);
275 policy.ssrc.value = 0;
276 policy.key = const_cast<uint8_t*>(key);
277 // TODO(astor) parse window size from WSH session-param
278 policy.window_size = 1024;
279 policy.allow_repeat_tx = 1;
280 // If external authentication option is enabled, supply custom auth module
281 // id EXTERNAL_HMAC_SHA1 in the policy structure.
282 // We want to set this option only for rtp packets.
283 // By default policy structure is initialized to HMAC_SHA1.
284 // Enable external HMAC authentication only for outgoing streams and only
285 // for cipher suites that support it (i.e. only non-GCM cipher suites).
286 if (type == ssrc_any_outbound && IsExternalAuthEnabled() &&
287 !rtc::IsGcmCryptoSuite(cs)) {
288 policy.rtp.auth_type = EXTERNAL_HMAC_SHA1;
289 }
Zhi Huangc99b6c72017-11-10 16:44:46 -0800290 if (!extension_ids.empty()) {
291 policy.enc_xtn_hdr = const_cast<int*>(&extension_ids[0]);
292 policy.enc_xtn_hdr_count = static_cast<int>(extension_ids.size());
zstein4dde3df2017-07-07 14:26:25 -0700293 }
294 policy.next = nullptr;
295
296 if (!session_) {
297 int err = srtp_create(&session_, &policy);
298 if (err != srtp_err_status_ok) {
299 session_ = nullptr;
Mirko Bonadei675513b2017-11-09 11:09:25 +0100300 RTC_LOG(LS_ERROR) << "Failed to create SRTP session, err=" << err;
zstein4dde3df2017-07-07 14:26:25 -0700301 return false;
302 }
303 srtp_set_user_data(session_, this);
304 } else {
305 int err = srtp_update(session_, &policy);
306 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100307 RTC_LOG(LS_ERROR) << "Failed to update SRTP session, err=" << err;
zstein4dde3df2017-07-07 14:26:25 -0700308 return false;
309 }
310 }
311
312 rtp_auth_tag_len_ = policy.rtp.auth_tag_len;
313 rtcp_auth_tag_len_ = policy.rtcp.auth_tag_len;
314 external_auth_active_ = (policy.rtp.auth_type == EXTERNAL_HMAC_SHA1);
315 return true;
316}
317
Zhi Huangc99b6c72017-11-10 16:44:46 -0800318bool SrtpSession::SetKey(int type,
319 int cs,
320 const uint8_t* key,
321 size_t len,
322 const std::vector<int>& extension_ids) {
zstein4dde3df2017-07-07 14:26:25 -0700323 RTC_DCHECK(thread_checker_.CalledOnValidThread());
324 if (session_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100325 RTC_LOG(LS_ERROR) << "Failed to create SRTP session: "
Jonas Olsson45cc8902018-02-13 10:37:07 +0100326 "SRTP session already created";
zstein4dde3df2017-07-07 14:26:25 -0700327 return false;
328 }
329
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -0700330 // This is the first time we need to actually interact with libsrtp, so
331 // initialize it if needed.
332 if (IncrementLibsrtpUsageCountAndMaybeInit()) {
333 inited_ = true;
334 } else {
zstein4dde3df2017-07-07 14:26:25 -0700335 return false;
336 }
337
Zhi Huangc99b6c72017-11-10 16:44:46 -0800338 return DoSetKey(type, cs, key, len, extension_ids);
zstein4dde3df2017-07-07 14:26:25 -0700339}
340
Zhi Huangc99b6c72017-11-10 16:44:46 -0800341bool SrtpSession::UpdateKey(int type,
342 int cs,
343 const uint8_t* key,
344 size_t len,
345 const std::vector<int>& extension_ids) {
zstein4dde3df2017-07-07 14:26:25 -0700346 RTC_DCHECK(thread_checker_.CalledOnValidThread());
347 if (!session_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100348 RTC_LOG(LS_ERROR) << "Failed to update non-existing SRTP session";
zstein4dde3df2017-07-07 14:26:25 -0700349 return false;
350 }
351
Zhi Huangc99b6c72017-11-10 16:44:46 -0800352 return DoSetKey(type, cs, key, len, extension_ids);
zstein4dde3df2017-07-07 14:26:25 -0700353}
354
Steve Antondb67ba12018-03-19 17:41:42 -0700355void SrtpSession::SetMetricsObserver(
356 rtc::scoped_refptr<webrtc::MetricsObserverInterface> metrics_observer) {
357 metrics_observer_ = metrics_observer;
358}
359
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -0700360int g_libsrtp_usage_count = 0;
361rtc::GlobalLockPod g_libsrtp_lock;
zstein4dde3df2017-07-07 14:26:25 -0700362
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -0700363// static
364bool SrtpSession::IncrementLibsrtpUsageCountAndMaybeInit() {
365 rtc::GlobalLockScope ls(&g_libsrtp_lock);
366
367 RTC_DCHECK_GE(g_libsrtp_usage_count, 0);
368 if (g_libsrtp_usage_count == 0) {
zstein4dde3df2017-07-07 14:26:25 -0700369 int err;
370 err = srtp_init();
371 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100372 RTC_LOG(LS_ERROR) << "Failed to init SRTP, err=" << err;
zstein4dde3df2017-07-07 14:26:25 -0700373 return false;
374 }
375
376 err = srtp_install_event_handler(&SrtpSession::HandleEventThunk);
377 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100378 RTC_LOG(LS_ERROR) << "Failed to install SRTP event handler, err=" << err;
zstein4dde3df2017-07-07 14:26:25 -0700379 return false;
380 }
381
382 err = external_crypto_init();
383 if (err != srtp_err_status_ok) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100384 RTC_LOG(LS_ERROR) << "Failed to initialize fake auth, err=" << err;
zstein4dde3df2017-07-07 14:26:25 -0700385 return false;
386 }
zstein4dde3df2017-07-07 14:26:25 -0700387 }
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -0700388 ++g_libsrtp_usage_count;
zstein4dde3df2017-07-07 14:26:25 -0700389 return true;
390}
391
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -0700392// static
393void SrtpSession::DecrementLibsrtpUsageCountAndMaybeDeinit() {
394 rtc::GlobalLockScope ls(&g_libsrtp_lock);
zstein4dde3df2017-07-07 14:26:25 -0700395
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -0700396 RTC_DCHECK_GE(g_libsrtp_usage_count, 1);
397 if (--g_libsrtp_usage_count == 0) {
zstein4dde3df2017-07-07 14:26:25 -0700398 int err = srtp_shutdown();
399 if (err) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100400 RTC_LOG(LS_ERROR) << "srtp_shutdown failed. err=" << err;
zstein4dde3df2017-07-07 14:26:25 -0700401 }
zstein4dde3df2017-07-07 14:26:25 -0700402 }
403}
404
405void SrtpSession::HandleEvent(const srtp_event_data_t* ev) {
406 RTC_DCHECK(thread_checker_.CalledOnValidThread());
407 switch (ev->event) {
408 case event_ssrc_collision:
Mirko Bonadei675513b2017-11-09 11:09:25 +0100409 RTC_LOG(LS_INFO) << "SRTP event: SSRC collision";
zstein4dde3df2017-07-07 14:26:25 -0700410 break;
411 case event_key_soft_limit:
Mirko Bonadei675513b2017-11-09 11:09:25 +0100412 RTC_LOG(LS_INFO) << "SRTP event: reached soft key usage limit";
zstein4dde3df2017-07-07 14:26:25 -0700413 break;
414 case event_key_hard_limit:
Mirko Bonadei675513b2017-11-09 11:09:25 +0100415 RTC_LOG(LS_INFO) << "SRTP event: reached hard key usage limit";
zstein4dde3df2017-07-07 14:26:25 -0700416 break;
417 case event_packet_index_limit:
Mirko Bonadei675513b2017-11-09 11:09:25 +0100418 RTC_LOG(LS_INFO)
419 << "SRTP event: reached hard packet limit (2^48 packets)";
zstein4dde3df2017-07-07 14:26:25 -0700420 break;
421 default:
Mirko Bonadei675513b2017-11-09 11:09:25 +0100422 RTC_LOG(LS_INFO) << "SRTP event: unknown " << ev->event;
zstein4dde3df2017-07-07 14:26:25 -0700423 break;
424 }
425}
426
427void SrtpSession::HandleEventThunk(srtp_event_data_t* ev) {
428 // Callback will be executed from same thread that calls the "srtp_protect"
429 // and "srtp_unprotect" functions.
430 SrtpSession* session =
431 static_cast<SrtpSession*>(srtp_get_user_data(ev->session));
432 if (session) {
433 session->HandleEvent(ev);
434 }
435}
436
437} // namespace cricket