blob: 4ba3619d9c0db37629370707ba6139e20558b725 [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
35bool SrtpSession::SetSend(int cs, const uint8_t* key, size_t len) {
36 return SetKey(ssrc_any_outbound, cs, key, len);
37}
38
39bool SrtpSession::UpdateSend(int cs, const uint8_t* key, size_t len) {
40 return UpdateKey(ssrc_any_outbound, cs, key, len);
41}
42
43bool SrtpSession::SetRecv(int cs, const uint8_t* key, size_t len) {
44 return SetKey(ssrc_any_inbound, cs, key, len);
45}
46
47bool SrtpSession::UpdateRecv(int cs, const uint8_t* key, size_t len) {
48 return UpdateKey(ssrc_any_inbound, cs, key, len);
49}
50
51bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
52 RTC_DCHECK(thread_checker_.CalledOnValidThread());
53 if (!session_) {
54 LOG(LS_WARNING) << "Failed to protect SRTP packet: no SRTP Session";
55 return false;
56 }
57
58 int need_len = in_len + rtp_auth_tag_len_; // NOLINT
59 if (max_len < need_len) {
60 LOG(LS_WARNING) << "Failed to protect SRTP packet: The buffer length "
61 << max_len << " is less than the needed " << need_len;
62 return false;
63 }
64
65 *out_len = in_len;
66 int err = srtp_protect(session_, p, out_len);
67 int seq_num;
68 GetRtpSeqNum(p, in_len, &seq_num);
69 if (err != srtp_err_status_ok) {
70 LOG(LS_WARNING) << "Failed to protect SRTP packet, seqnum=" << seq_num
71 << ", err=" << err
72 << ", last seqnum=" << last_send_seq_num_;
73 return false;
74 }
75 last_send_seq_num_ = seq_num;
76 return true;
77}
78
79bool SrtpSession::ProtectRtp(void* p,
80 int in_len,
81 int max_len,
82 int* out_len,
83 int64_t* index) {
84 if (!ProtectRtp(p, in_len, max_len, out_len)) {
85 return false;
86 }
87 return (index) ? GetSendStreamPacketIndex(p, in_len, index) : true;
88}
89
90bool SrtpSession::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) {
91 RTC_DCHECK(thread_checker_.CalledOnValidThread());
92 if (!session_) {
93 LOG(LS_WARNING) << "Failed to protect SRTCP packet: no SRTP Session";
94 return false;
95 }
96
97 int need_len = in_len + sizeof(uint32_t) + rtcp_auth_tag_len_; // NOLINT
98 if (max_len < need_len) {
99 LOG(LS_WARNING) << "Failed to protect SRTCP packet: The buffer length "
100 << max_len << " is less than the needed " << need_len;
101 return false;
102 }
103
104 *out_len = in_len;
105 int err = srtp_protect_rtcp(session_, p, out_len);
106 if (err != srtp_err_status_ok) {
107 LOG(LS_WARNING) << "Failed to protect SRTCP packet, err=" << err;
108 return false;
109 }
110 return true;
111}
112
113bool SrtpSession::UnprotectRtp(void* p, int in_len, int* out_len) {
114 RTC_DCHECK(thread_checker_.CalledOnValidThread());
115 if (!session_) {
116 LOG(LS_WARNING) << "Failed to unprotect SRTP packet: no SRTP Session";
117 return false;
118 }
119
120 *out_len = in_len;
121 int err = srtp_unprotect(session_, p, out_len);
122 if (err != srtp_err_status_ok) {
123 LOG(LS_WARNING) << "Failed to unprotect SRTP packet, err=" << err;
124 return false;
125 }
126 return true;
127}
128
129bool SrtpSession::UnprotectRtcp(void* p, int in_len, int* out_len) {
130 RTC_DCHECK(thread_checker_.CalledOnValidThread());
131 if (!session_) {
132 LOG(LS_WARNING) << "Failed to unprotect SRTCP packet: no SRTP Session";
133 return false;
134 }
135
136 *out_len = in_len;
137 int err = srtp_unprotect_rtcp(session_, p, out_len);
138 if (err != srtp_err_status_ok) {
139 LOG(LS_WARNING) << "Failed to unprotect SRTCP packet, err=" << err;
140 return false;
141 }
142 return true;
143}
144
145bool SrtpSession::GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len) {
146 RTC_DCHECK(thread_checker_.CalledOnValidThread());
147 RTC_DCHECK(IsExternalAuthActive());
148 if (!IsExternalAuthActive()) {
149 return false;
150 }
151
152 ExternalHmacContext* external_hmac = nullptr;
153 // stream_template will be the reference context for other streams.
154 // Let's use it for getting the keys.
155 srtp_stream_ctx_t* srtp_context = session_->stream_template;
zstein4dde3df2017-07-07 14:26:25 -0700156 if (srtp_context && srtp_context->session_keys &&
157 srtp_context->session_keys->rtp_auth) {
158 external_hmac = reinterpret_cast<ExternalHmacContext*>(
159 srtp_context->session_keys->rtp_auth->state);
160 }
zstein4dde3df2017-07-07 14:26:25 -0700161
162 if (!external_hmac) {
163 LOG(LS_ERROR) << "Failed to get auth keys from libsrtp!.";
164 return false;
165 }
166
167 *key = external_hmac->key;
168 *key_len = external_hmac->key_length;
169 *tag_len = rtp_auth_tag_len_;
170 return true;
171}
172
173int SrtpSession::GetSrtpOverhead() const {
174 return rtp_auth_tag_len_;
175}
176
177void SrtpSession::EnableExternalAuth() {
178 RTC_DCHECK(!session_);
179 external_auth_enabled_ = true;
180}
181
182bool SrtpSession::IsExternalAuthEnabled() const {
183 return external_auth_enabled_;
184}
185
186bool SrtpSession::IsExternalAuthActive() const {
187 return external_auth_active_;
188}
189
190bool SrtpSession::GetSendStreamPacketIndex(void* p,
191 int in_len,
192 int64_t* index) {
193 RTC_DCHECK(thread_checker_.CalledOnValidThread());
194 srtp_hdr_t* hdr = reinterpret_cast<srtp_hdr_t*>(p);
195 srtp_stream_ctx_t* stream = srtp_get_stream(session_, hdr->ssrc);
196 if (!stream) {
197 return false;
198 }
199
200 // Shift packet index, put into network byte order
201 *index = static_cast<int64_t>(rtc::NetworkToHost64(
202 srtp_rdbx_get_packet_index(&stream->rtp_rdbx) << 16));
203 return true;
204}
205
206bool SrtpSession::DoSetKey(int type, int cs, const uint8_t* key, size_t len) {
207 RTC_DCHECK(thread_checker_.CalledOnValidThread());
208
209 srtp_policy_t policy;
210 memset(&policy, 0, sizeof(policy));
211 if (cs == rtc::SRTP_AES128_CM_SHA1_80) {
212 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
213 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
214 } else if (cs == rtc::SRTP_AES128_CM_SHA1_32) {
215 // RTP HMAC is shortened to 32 bits, but RTCP remains 80 bits.
216 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp);
217 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
218 } else if (cs == rtc::SRTP_AEAD_AES_128_GCM) {
219 srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtp);
220 srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtcp);
221 } else if (cs == rtc::SRTP_AEAD_AES_256_GCM) {
222 srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp);
223 srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtcp);
224 } else {
225 LOG(LS_WARNING) << "Failed to " << (session_ ? "update" : "create")
226 << " SRTP session: unsupported cipher_suite " << cs;
227 return false;
228 }
229
230 int expected_key_len;
231 int expected_salt_len;
232 if (!rtc::GetSrtpKeyAndSaltLengths(cs, &expected_key_len,
233 &expected_salt_len)) {
234 // This should never happen.
235 LOG(LS_WARNING)
236 << "Failed to " << (session_ ? "update" : "create")
237 << " SRTP session: unsupported cipher_suite without length information"
238 << cs;
239 return false;
240 }
241
242 if (!key ||
243 len != static_cast<size_t>(expected_key_len + expected_salt_len)) {
244 LOG(LS_WARNING) << "Failed to " << (session_ ? "update" : "create")
245 << " SRTP session: invalid key";
246 return false;
247 }
248
249 policy.ssrc.type = static_cast<srtp_ssrc_type_t>(type);
250 policy.ssrc.value = 0;
251 policy.key = const_cast<uint8_t*>(key);
252 // TODO(astor) parse window size from WSH session-param
253 policy.window_size = 1024;
254 policy.allow_repeat_tx = 1;
255 // If external authentication option is enabled, supply custom auth module
256 // id EXTERNAL_HMAC_SHA1 in the policy structure.
257 // We want to set this option only for rtp packets.
258 // By default policy structure is initialized to HMAC_SHA1.
259 // Enable external HMAC authentication only for outgoing streams and only
260 // for cipher suites that support it (i.e. only non-GCM cipher suites).
261 if (type == ssrc_any_outbound && IsExternalAuthEnabled() &&
262 !rtc::IsGcmCryptoSuite(cs)) {
263 policy.rtp.auth_type = EXTERNAL_HMAC_SHA1;
264 }
265 if (!encrypted_header_extension_ids_.empty()) {
266 policy.enc_xtn_hdr = const_cast<int*>(&encrypted_header_extension_ids_[0]);
267 policy.enc_xtn_hdr_count =
268 static_cast<int>(encrypted_header_extension_ids_.size());
269 }
270 policy.next = nullptr;
271
272 if (!session_) {
273 int err = srtp_create(&session_, &policy);
274 if (err != srtp_err_status_ok) {
275 session_ = nullptr;
276 LOG(LS_ERROR) << "Failed to create SRTP session, err=" << err;
277 return false;
278 }
279 srtp_set_user_data(session_, this);
280 } else {
281 int err = srtp_update(session_, &policy);
282 if (err != srtp_err_status_ok) {
283 LOG(LS_ERROR) << "Failed to update SRTP session, err=" << err;
284 return false;
285 }
286 }
287
288 rtp_auth_tag_len_ = policy.rtp.auth_tag_len;
289 rtcp_auth_tag_len_ = policy.rtcp.auth_tag_len;
290 external_auth_active_ = (policy.rtp.auth_type == EXTERNAL_HMAC_SHA1);
291 return true;
292}
293
294bool SrtpSession::SetKey(int type, int cs, const uint8_t* key, size_t len) {
295 RTC_DCHECK(thread_checker_.CalledOnValidThread());
296 if (session_) {
297 LOG(LS_ERROR) << "Failed to create SRTP session: "
298 << "SRTP session already created";
299 return false;
300 }
301
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -0700302 // This is the first time we need to actually interact with libsrtp, so
303 // initialize it if needed.
304 if (IncrementLibsrtpUsageCountAndMaybeInit()) {
305 inited_ = true;
306 } else {
zstein4dde3df2017-07-07 14:26:25 -0700307 return false;
308 }
309
310 return DoSetKey(type, cs, key, len);
311}
312
313bool SrtpSession::UpdateKey(int type, int cs, const uint8_t* key, size_t len) {
314 RTC_DCHECK(thread_checker_.CalledOnValidThread());
315 if (!session_) {
316 LOG(LS_ERROR) << "Failed to update non-existing SRTP session";
317 return false;
318 }
319
320 return DoSetKey(type, cs, key, len);
321}
322
323void SrtpSession::SetEncryptedHeaderExtensionIds(
324 const std::vector<int>& encrypted_header_extension_ids) {
325 RTC_DCHECK(thread_checker_.CalledOnValidThread());
326 encrypted_header_extension_ids_ = encrypted_header_extension_ids;
327}
328
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -0700329int g_libsrtp_usage_count = 0;
330rtc::GlobalLockPod g_libsrtp_lock;
zstein4dde3df2017-07-07 14:26:25 -0700331
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -0700332// static
333bool SrtpSession::IncrementLibsrtpUsageCountAndMaybeInit() {
334 rtc::GlobalLockScope ls(&g_libsrtp_lock);
335
336 RTC_DCHECK_GE(g_libsrtp_usage_count, 0);
337 if (g_libsrtp_usage_count == 0) {
zstein4dde3df2017-07-07 14:26:25 -0700338 int err;
339 err = srtp_init();
340 if (err != srtp_err_status_ok) {
341 LOG(LS_ERROR) << "Failed to init SRTP, err=" << err;
342 return false;
343 }
344
345 err = srtp_install_event_handler(&SrtpSession::HandleEventThunk);
346 if (err != srtp_err_status_ok) {
347 LOG(LS_ERROR) << "Failed to install SRTP event handler, err=" << err;
348 return false;
349 }
350
351 err = external_crypto_init();
352 if (err != srtp_err_status_ok) {
353 LOG(LS_ERROR) << "Failed to initialize fake auth, err=" << err;
354 return false;
355 }
zstein4dde3df2017-07-07 14:26:25 -0700356 }
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -0700357 ++g_libsrtp_usage_count;
zstein4dde3df2017-07-07 14:26:25 -0700358 return true;
359}
360
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -0700361// static
362void SrtpSession::DecrementLibsrtpUsageCountAndMaybeDeinit() {
363 rtc::GlobalLockScope ls(&g_libsrtp_lock);
zstein4dde3df2017-07-07 14:26:25 -0700364
Taylor Brandstetterb140b9f2017-10-12 17:24:16 -0700365 RTC_DCHECK_GE(g_libsrtp_usage_count, 1);
366 if (--g_libsrtp_usage_count == 0) {
zstein4dde3df2017-07-07 14:26:25 -0700367 int err = srtp_shutdown();
368 if (err) {
369 LOG(LS_ERROR) << "srtp_shutdown failed. err=" << err;
zstein4dde3df2017-07-07 14:26:25 -0700370 }
zstein4dde3df2017-07-07 14:26:25 -0700371 }
372}
373
374void SrtpSession::HandleEvent(const srtp_event_data_t* ev) {
375 RTC_DCHECK(thread_checker_.CalledOnValidThread());
376 switch (ev->event) {
377 case event_ssrc_collision:
378 LOG(LS_INFO) << "SRTP event: SSRC collision";
379 break;
380 case event_key_soft_limit:
381 LOG(LS_INFO) << "SRTP event: reached soft key usage limit";
382 break;
383 case event_key_hard_limit:
384 LOG(LS_INFO) << "SRTP event: reached hard key usage limit";
385 break;
386 case event_packet_index_limit:
387 LOG(LS_INFO) << "SRTP event: reached hard packet limit (2^48 packets)";
388 break;
389 default:
390 LOG(LS_INFO) << "SRTP event: unknown " << ev->event;
391 break;
392 }
393}
394
395void SrtpSession::HandleEventThunk(srtp_event_data_t* ev) {
396 // Callback will be executed from same thread that calls the "srtp_protect"
397 // and "srtp_unprotect" functions.
398 SrtpSession* session =
399 static_cast<SrtpSession*>(srtp_get_user_data(ev->session));
400 if (session) {
401 session->HandleEvent(ev);
402 }
403}
404
405} // namespace cricket