blob: d4ad3bb0633ef43c46d21dfec9956cdddf9da90d [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellander65c7f672016-02-12 00:05:01 -08002 * Copyright 2009 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellander65c7f672016-02-12 00:05:01 -08004 * 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.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
Steve Anton10542f22019-01-11 09:11:00 -080011#include "pc/srtp_filter.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
pbos@webrtc.org371243d2014-03-07 15:22:04 +000013#include <string.h>
Jonas Olssona4d87372019-07-05 19:08:33 +020014
Yves Gerey3e707812018-11-28 16:47:49 +010015#include <cstdint>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000016
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "rtc_base/logging.h"
Steve Anton10542f22019-01-11 09:11:00 -080018#include "rtc_base/ssl_stream_adapter.h"
Artem Titova76af0c2018-07-23 17:38:12 +020019#include "rtc_base/third_party/base64/base64.h"
Joachim Bauch5b32f232018-03-07 20:02:26 +010020#include "rtc_base/zero_memory.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000021
henrike@webrtc.org28e20752013-07-10 00:45:36 +000022namespace cricket {
23
Yves Gerey665174f2018-06-19 15:03:05 +020024SrtpFilter::SrtpFilter() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +000025
Yves Gerey665174f2018-06-19 15:03:05 +020026SrtpFilter::~SrtpFilter() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +000027
28bool SrtpFilter::IsActive() const {
29 return state_ >= ST_ACTIVE;
30}
31
Zhi Huange818b6e2018-02-22 15:26:27 -080032bool SrtpFilter::Process(const std::vector<CryptoParams>& cryptos,
33 webrtc::SdpType type,
34 ContentSource source) {
35 bool ret = false;
36 switch (type) {
37 case webrtc::SdpType::kOffer:
38 ret = SetOffer(cryptos, source);
39 break;
40 case webrtc::SdpType::kPrAnswer:
41 ret = SetProvisionalAnswer(cryptos, source);
42 break;
43 case webrtc::SdpType::kAnswer:
44 ret = SetAnswer(cryptos, source);
45 break;
46 default:
47 break;
48 }
49
50 if (!ret) {
51 return false;
52 }
53
54 return true;
55}
56
henrike@webrtc.org28e20752013-07-10 00:45:36 +000057bool SrtpFilter::SetOffer(const std::vector<CryptoParams>& offer_params,
58 ContentSource source) {
59 if (!ExpectOffer(source)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010060 RTC_LOG(LS_ERROR) << "Wrong state to update SRTP offer";
61 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000062 }
63 return StoreParams(offer_params, source);
64}
65
66bool SrtpFilter::SetAnswer(const std::vector<CryptoParams>& answer_params,
67 ContentSource source) {
68 return DoSetAnswer(answer_params, source, true);
69}
70
71bool SrtpFilter::SetProvisionalAnswer(
72 const std::vector<CryptoParams>& answer_params,
73 ContentSource source) {
74 return DoSetAnswer(answer_params, source, false);
75}
76
henrike@webrtc.org28e20752013-07-10 00:45:36 +000077bool SrtpFilter::ExpectOffer(ContentSource source) {
Yves Gerey665174f2018-06-19 15:03:05 +020078 return ((state_ == ST_INIT) || (state_ == ST_ACTIVE) ||
79 (state_ == ST_SENTOFFER && source == CS_LOCAL) ||
80 (state_ == ST_SENTUPDATEDOFFER && source == CS_LOCAL) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +000081 (state_ == ST_RECEIVEDOFFER && source == CS_REMOTE) ||
82 (state_ == ST_RECEIVEDUPDATEDOFFER && source == CS_REMOTE));
83}
84
85bool SrtpFilter::StoreParams(const std::vector<CryptoParams>& params,
86 ContentSource source) {
87 offer_params_ = params;
88 if (state_ == ST_INIT) {
89 state_ = (source == CS_LOCAL) ? ST_SENTOFFER : ST_RECEIVEDOFFER;
pthatcher@webrtc.org2e7ee4b2014-10-27 16:10:29 +000090 } else if (state_ == ST_ACTIVE) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000091 state_ =
92 (source == CS_LOCAL) ? ST_SENTUPDATEDOFFER : ST_RECEIVEDUPDATEDOFFER;
93 }
94 return true;
95}
96
97bool SrtpFilter::ExpectAnswer(ContentSource source) {
98 return ((state_ == ST_SENTOFFER && source == CS_REMOTE) ||
99 (state_ == ST_RECEIVEDOFFER && source == CS_LOCAL) ||
100 (state_ == ST_SENTUPDATEDOFFER && source == CS_REMOTE) ||
101 (state_ == ST_RECEIVEDUPDATEDOFFER && source == CS_LOCAL) ||
102 (state_ == ST_SENTPRANSWER_NO_CRYPTO && source == CS_LOCAL) ||
103 (state_ == ST_SENTPRANSWER && source == CS_LOCAL) ||
104 (state_ == ST_RECEIVEDPRANSWER_NO_CRYPTO && source == CS_REMOTE) ||
105 (state_ == ST_RECEIVEDPRANSWER && source == CS_REMOTE));
106}
107
108bool SrtpFilter::DoSetAnswer(const std::vector<CryptoParams>& answer_params,
109 ContentSource source,
110 bool final) {
111 if (!ExpectAnswer(source)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100112 RTC_LOG(LS_ERROR) << "Invalid state for SRTP answer";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000113 return false;
114 }
115
116 // If the answer doesn't requests crypto complete the negotiation of an
117 // unencrypted session.
118 // Otherwise, finalize the parameters and apply them.
119 if (answer_params.empty()) {
120 if (final) {
121 return ResetParams();
122 } else {
123 // Need to wait for the final answer to decide if
124 // we should go to Active state.
Yves Gerey665174f2018-06-19 15:03:05 +0200125 state_ = (source == CS_LOCAL) ? ST_SENTPRANSWER_NO_CRYPTO
126 : ST_RECEIVEDPRANSWER_NO_CRYPTO;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000127 return true;
128 }
129 }
130 CryptoParams selected_params;
131 if (!NegotiateParams(answer_params, &selected_params))
132 return false;
Zhi Huangcf990f52017-09-22 12:12:30 -0700133
134 const CryptoParams& new_send_params =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000135 (source == CS_REMOTE) ? selected_params : answer_params[0];
Zhi Huangcf990f52017-09-22 12:12:30 -0700136 const CryptoParams& new_recv_params =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000137 (source == CS_REMOTE) ? answer_params[0] : selected_params;
Zhi Huangcf990f52017-09-22 12:12:30 -0700138 if (!ApplySendParams(new_send_params) || !ApplyRecvParams(new_recv_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000139 return false;
140 }
Zhi Huangcf990f52017-09-22 12:12:30 -0700141 applied_send_params_ = new_send_params;
142 applied_recv_params_ = new_recv_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000143
144 if (final) {
145 offer_params_.clear();
146 state_ = ST_ACTIVE;
147 } else {
Yves Gerey665174f2018-06-19 15:03:05 +0200148 state_ = (source == CS_LOCAL) ? ST_SENTPRANSWER : ST_RECEIVEDPRANSWER;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000149 }
150 return true;
151}
152
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000153bool SrtpFilter::NegotiateParams(const std::vector<CryptoParams>& answer_params,
154 CryptoParams* selected_params) {
155 // We're processing an accept. We should have exactly one set of params,
156 // unless the offer didn't mention crypto, in which case we shouldn't be here.
157 bool ret = (answer_params.size() == 1U && !offer_params_.empty());
158 if (ret) {
159 // We should find a match between the answer params and the offered params.
160 std::vector<CryptoParams>::const_iterator it;
161 for (it = offer_params_.begin(); it != offer_params_.end(); ++it) {
162 if (answer_params[0].Matches(*it)) {
163 break;
164 }
165 }
166
167 if (it != offer_params_.end()) {
168 *selected_params = *it;
169 } else {
170 ret = false;
171 }
172 }
173
174 if (!ret) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100175 RTC_LOG(LS_WARNING) << "Invalid parameters in SRTP answer";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000176 }
177 return ret;
178}
179
Zhi Huangcf990f52017-09-22 12:12:30 -0700180bool SrtpFilter::ResetParams() {
181 offer_params_.clear();
182 applied_send_params_ = CryptoParams();
183 applied_recv_params_ = CryptoParams();
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200184 send_cipher_suite_ = absl::nullopt;
185 recv_cipher_suite_ = absl::nullopt;
Zhi Huangcf990f52017-09-22 12:12:30 -0700186 send_key_.Clear();
187 recv_key_.Clear();
188 state_ = ST_INIT;
189 return true;
190}
191
192bool SrtpFilter::ApplySendParams(const CryptoParams& send_params) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000193 if (applied_send_params_.cipher_suite == send_params.cipher_suite &&
Zhi Huangcf990f52017-09-22 12:12:30 -0700194 applied_send_params_.key_params == send_params.key_params) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100195 RTC_LOG(LS_INFO) << "Applying the same SRTP send parameters again. No-op.";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000196
197 // We do not want to reset the ROC if the keys are the same. So just return.
198 return true;
199 }
jbauchcb560652016-08-04 05:20:32 -0700200
Oskar Sundbom36f8f3e2017-11-16 10:54:27 +0100201 send_cipher_suite_ = rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite);
Zhi Huangcf990f52017-09-22 12:12:30 -0700202 if (send_cipher_suite_ == rtc::SRTP_INVALID_CRYPTO_SUITE) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100203 RTC_LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
Jonas Olsson45cc8902018-02-13 10:37:07 +0100204 " send cipher_suite "
205 << send_params.cipher_suite;
jbauchcb560652016-08-04 05:20:32 -0700206 return false;
207 }
208
209 int send_key_len, send_salt_len;
Zhi Huangcf990f52017-09-22 12:12:30 -0700210 if (!rtc::GetSrtpKeyAndSaltLengths(*send_cipher_suite_, &send_key_len,
211 &send_salt_len)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100212 RTC_LOG(LS_WARNING) << "Could not get lengths for crypto suite(s):"
Jonas Olsson45cc8902018-02-13 10:37:07 +0100213 " send cipher_suite "
214 << send_params.cipher_suite;
Zhi Huangcf990f52017-09-22 12:12:30 -0700215 return false;
216 }
217
Joachim Bauch5b32f232018-03-07 20:02:26 +0100218 send_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(send_key_len + send_salt_len);
Zhi Huangcf990f52017-09-22 12:12:30 -0700219 return ParseKeyParams(send_params.key_params, send_key_.data(),
220 send_key_.size());
221}
222
223bool SrtpFilter::ApplyRecvParams(const CryptoParams& recv_params) {
224 if (applied_recv_params_.cipher_suite == recv_params.cipher_suite &&
225 applied_recv_params_.key_params == recv_params.key_params) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100226 RTC_LOG(LS_INFO) << "Applying the same SRTP recv parameters again. No-op.";
Zhi Huangcf990f52017-09-22 12:12:30 -0700227
228 // We do not want to reset the ROC if the keys are the same. So just return.
229 return true;
230 }
231
Oskar Sundbom36f8f3e2017-11-16 10:54:27 +0100232 recv_cipher_suite_ = rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite);
Zhi Huangcf990f52017-09-22 12:12:30 -0700233 if (recv_cipher_suite_ == rtc::SRTP_INVALID_CRYPTO_SUITE) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100234 RTC_LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
Jonas Olsson45cc8902018-02-13 10:37:07 +0100235 " recv cipher_suite "
236 << recv_params.cipher_suite;
zhihuange683c682017-08-31 16:00:07 -0700237 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000238 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000239
Zhi Huangcf990f52017-09-22 12:12:30 -0700240 int recv_key_len, recv_salt_len;
241 if (!rtc::GetSrtpKeyAndSaltLengths(*recv_cipher_suite_, &recv_key_len,
242 &recv_salt_len)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100243 RTC_LOG(LS_WARNING) << "Could not get lengths for crypto suite(s):"
Jonas Olsson45cc8902018-02-13 10:37:07 +0100244 " recv cipher_suite "
245 << recv_params.cipher_suite;
Zhi Huangcf990f52017-09-22 12:12:30 -0700246 return false;
zhihuangeb23e172017-09-19 01:12:52 -0700247 }
zhihuangeb23e172017-09-19 01:12:52 -0700248
Joachim Bauch5b32f232018-03-07 20:02:26 +0100249 recv_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(recv_key_len + recv_salt_len);
Zhi Huangcf990f52017-09-22 12:12:30 -0700250 return ParseKeyParams(recv_params.key_params, recv_key_.data(),
251 recv_key_.size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000252}
253
254bool SrtpFilter::ParseKeyParams(const std::string& key_params,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200255 uint8_t* key,
jbauchcb560652016-08-04 05:20:32 -0700256 size_t len) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000257 // example key_params: "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2"
258
259 // Fail if key-method is wrong.
260 if (key_params.find("inline:") != 0) {
261 return false;
262 }
263
264 // Fail if base64 decode fails, or the key is the wrong size.
265 std::string key_b64(key_params.substr(7)), key_str;
zhihuange683c682017-08-31 16:00:07 -0700266 if (!rtc::Base64::Decode(key_b64, rtc::Base64::DO_STRICT, &key_str,
267 nullptr) ||
268 key_str.size() != len) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000269 return false;
270 }
271
272 memcpy(key, key_str.c_str(), len);
Joachim Bauch5b32f232018-03-07 20:02:26 +0100273 // TODO(bugs.webrtc.org/8905): Switch to ZeroOnFreeBuffer for storing
274 // sensitive data.
275 rtc::ExplicitZeroMemory(&key_str[0], key_str.size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000276 return true;
277}
278
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000279} // namespace cricket