1 /*
2 * Copyright 2009 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_filter.h"
12
13 #include <string.h>
14
15 #include <cstdint>
16
17 #include "absl/strings/match.h"
18 #include "rtc_base/logging.h"
19 #include "rtc_base/ssl_stream_adapter.h"
20 #include "rtc_base/third_party/base64/base64.h"
21 #include "rtc_base/zero_memory.h"
22
23 namespace cricket {
24
SrtpFilter()25 SrtpFilter::SrtpFilter() {}
26
~SrtpFilter()27 SrtpFilter::~SrtpFilter() {}
28
IsActive() const29 bool SrtpFilter::IsActive() const {
30 return state_ >= ST_ACTIVE;
31 }
32
Process(const std::vector<CryptoParams> & cryptos,webrtc::SdpType type,ContentSource source)33 bool SrtpFilter::Process(const std::vector<CryptoParams>& cryptos,
34 webrtc::SdpType type,
35 ContentSource source) {
36 bool ret = false;
37 switch (type) {
38 case webrtc::SdpType::kOffer:
39 ret = SetOffer(cryptos, source);
40 break;
41 case webrtc::SdpType::kPrAnswer:
42 ret = SetProvisionalAnswer(cryptos, source);
43 break;
44 case webrtc::SdpType::kAnswer:
45 ret = SetAnswer(cryptos, source);
46 break;
47 default:
48 break;
49 }
50
51 if (!ret) {
52 return false;
53 }
54
55 return true;
56 }
57
SetOffer(const std::vector<CryptoParams> & offer_params,ContentSource source)58 bool SrtpFilter::SetOffer(const std::vector<CryptoParams>& offer_params,
59 ContentSource source) {
60 if (!ExpectOffer(source)) {
61 RTC_LOG(LS_ERROR) << "Wrong state to update SRTP offer";
62 return false;
63 }
64 return StoreParams(offer_params, source);
65 }
66
SetAnswer(const std::vector<CryptoParams> & answer_params,ContentSource source)67 bool SrtpFilter::SetAnswer(const std::vector<CryptoParams>& answer_params,
68 ContentSource source) {
69 return DoSetAnswer(answer_params, source, true);
70 }
71
SetProvisionalAnswer(const std::vector<CryptoParams> & answer_params,ContentSource source)72 bool SrtpFilter::SetProvisionalAnswer(
73 const std::vector<CryptoParams>& answer_params,
74 ContentSource source) {
75 return DoSetAnswer(answer_params, source, false);
76 }
77
ExpectOffer(ContentSource source)78 bool SrtpFilter::ExpectOffer(ContentSource source) {
79 return ((state_ == ST_INIT) || (state_ == ST_ACTIVE) ||
80 (state_ == ST_SENTOFFER && source == CS_LOCAL) ||
81 (state_ == ST_SENTUPDATEDOFFER && source == CS_LOCAL) ||
82 (state_ == ST_RECEIVEDOFFER && source == CS_REMOTE) ||
83 (state_ == ST_RECEIVEDUPDATEDOFFER && source == CS_REMOTE));
84 }
85
StoreParams(const std::vector<CryptoParams> & params,ContentSource source)86 bool SrtpFilter::StoreParams(const std::vector<CryptoParams>& params,
87 ContentSource source) {
88 offer_params_ = params;
89 if (state_ == ST_INIT) {
90 state_ = (source == CS_LOCAL) ? ST_SENTOFFER : ST_RECEIVEDOFFER;
91 } else if (state_ == ST_ACTIVE) {
92 state_ =
93 (source == CS_LOCAL) ? ST_SENTUPDATEDOFFER : ST_RECEIVEDUPDATEDOFFER;
94 }
95 return true;
96 }
97
ExpectAnswer(ContentSource source)98 bool SrtpFilter::ExpectAnswer(ContentSource source) {
99 return ((state_ == ST_SENTOFFER && source == CS_REMOTE) ||
100 (state_ == ST_RECEIVEDOFFER && source == CS_LOCAL) ||
101 (state_ == ST_SENTUPDATEDOFFER && source == CS_REMOTE) ||
102 (state_ == ST_RECEIVEDUPDATEDOFFER && source == CS_LOCAL) ||
103 (state_ == ST_SENTPRANSWER_NO_CRYPTO && source == CS_LOCAL) ||
104 (state_ == ST_SENTPRANSWER && source == CS_LOCAL) ||
105 (state_ == ST_RECEIVEDPRANSWER_NO_CRYPTO && source == CS_REMOTE) ||
106 (state_ == ST_RECEIVEDPRANSWER && source == CS_REMOTE));
107 }
108
DoSetAnswer(const std::vector<CryptoParams> & answer_params,ContentSource source,bool final)109 bool SrtpFilter::DoSetAnswer(const std::vector<CryptoParams>& answer_params,
110 ContentSource source,
111 bool final) {
112 if (!ExpectAnswer(source)) {
113 RTC_LOG(LS_ERROR) << "Invalid state for SRTP answer";
114 return false;
115 }
116
117 // If the answer doesn't requests crypto complete the negotiation of an
118 // unencrypted session.
119 // Otherwise, finalize the parameters and apply them.
120 if (answer_params.empty()) {
121 if (final) {
122 return ResetParams();
123 } else {
124 // Need to wait for the final answer to decide if
125 // we should go to Active state.
126 state_ = (source == CS_LOCAL) ? ST_SENTPRANSWER_NO_CRYPTO
127 : ST_RECEIVEDPRANSWER_NO_CRYPTO;
128 return true;
129 }
130 }
131 CryptoParams selected_params;
132 if (!NegotiateParams(answer_params, &selected_params))
133 return false;
134
135 const CryptoParams& new_send_params =
136 (source == CS_REMOTE) ? selected_params : answer_params[0];
137 const CryptoParams& new_recv_params =
138 (source == CS_REMOTE) ? answer_params[0] : selected_params;
139 if (!ApplySendParams(new_send_params) || !ApplyRecvParams(new_recv_params)) {
140 return false;
141 }
142 applied_send_params_ = new_send_params;
143 applied_recv_params_ = new_recv_params;
144
145 if (final) {
146 offer_params_.clear();
147 state_ = ST_ACTIVE;
148 } else {
149 state_ = (source == CS_LOCAL) ? ST_SENTPRANSWER : ST_RECEIVEDPRANSWER;
150 }
151 return true;
152 }
153
NegotiateParams(const std::vector<CryptoParams> & answer_params,CryptoParams * selected_params)154 bool SrtpFilter::NegotiateParams(const std::vector<CryptoParams>& answer_params,
155 CryptoParams* selected_params) {
156 // We're processing an accept. We should have exactly one set of params,
157 // unless the offer didn't mention crypto, in which case we shouldn't be here.
158 bool ret = (answer_params.size() == 1U && !offer_params_.empty());
159 if (ret) {
160 // We should find a match between the answer params and the offered params.
161 std::vector<CryptoParams>::const_iterator it;
162 for (it = offer_params_.begin(); it != offer_params_.end(); ++it) {
163 if (answer_params[0].Matches(*it)) {
164 break;
165 }
166 }
167
168 if (it != offer_params_.end()) {
169 *selected_params = *it;
170 } else {
171 ret = false;
172 }
173 }
174
175 if (!ret) {
176 RTC_LOG(LS_WARNING) << "Invalid parameters in SRTP answer";
177 }
178 return ret;
179 }
180
ResetParams()181 bool SrtpFilter::ResetParams() {
182 offer_params_.clear();
183 applied_send_params_ = CryptoParams();
184 applied_recv_params_ = CryptoParams();
185 send_cipher_suite_ = absl::nullopt;
186 recv_cipher_suite_ = absl::nullopt;
187 send_key_.Clear();
188 recv_key_.Clear();
189 state_ = ST_INIT;
190 return true;
191 }
192
ApplySendParams(const CryptoParams & send_params)193 bool SrtpFilter::ApplySendParams(const CryptoParams& send_params) {
194 if (applied_send_params_.cipher_suite == send_params.cipher_suite &&
195 applied_send_params_.key_params == send_params.key_params) {
196 RTC_LOG(LS_INFO) << "Applying the same SRTP send parameters again. No-op.";
197
198 // We do not want to reset the ROC if the keys are the same. So just return.
199 return true;
200 }
201
202 send_cipher_suite_ = rtc::SrtpCryptoSuiteFromName(send_params.cipher_suite);
203 if (send_cipher_suite_ == rtc::SRTP_INVALID_CRYPTO_SUITE) {
204 RTC_LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
205 " send cipher_suite "
206 << send_params.cipher_suite;
207 return false;
208 }
209
210 int send_key_len, send_salt_len;
211 if (!rtc::GetSrtpKeyAndSaltLengths(*send_cipher_suite_, &send_key_len,
212 &send_salt_len)) {
213 RTC_LOG(LS_WARNING) << "Could not get lengths for crypto suite(s):"
214 " send cipher_suite "
215 << send_params.cipher_suite;
216 return false;
217 }
218
219 send_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(send_key_len + send_salt_len);
220 return ParseKeyParams(send_params.key_params, send_key_.data(),
221 send_key_.size());
222 }
223
ApplyRecvParams(const CryptoParams & recv_params)224 bool SrtpFilter::ApplyRecvParams(const CryptoParams& recv_params) {
225 if (applied_recv_params_.cipher_suite == recv_params.cipher_suite &&
226 applied_recv_params_.key_params == recv_params.key_params) {
227 RTC_LOG(LS_INFO) << "Applying the same SRTP recv parameters again. No-op.";
228
229 // We do not want to reset the ROC if the keys are the same. So just return.
230 return true;
231 }
232
233 recv_cipher_suite_ = rtc::SrtpCryptoSuiteFromName(recv_params.cipher_suite);
234 if (recv_cipher_suite_ == rtc::SRTP_INVALID_CRYPTO_SUITE) {
235 RTC_LOG(LS_WARNING) << "Unknown crypto suite(s) received:"
236 " recv cipher_suite "
237 << recv_params.cipher_suite;
238 return false;
239 }
240
241 int recv_key_len, recv_salt_len;
242 if (!rtc::GetSrtpKeyAndSaltLengths(*recv_cipher_suite_, &recv_key_len,
243 &recv_salt_len)) {
244 RTC_LOG(LS_WARNING) << "Could not get lengths for crypto suite(s):"
245 " recv cipher_suite "
246 << recv_params.cipher_suite;
247 return false;
248 }
249
250 recv_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(recv_key_len + recv_salt_len);
251 return ParseKeyParams(recv_params.key_params, recv_key_.data(),
252 recv_key_.size());
253 }
254
ParseKeyParams(const std::string & key_params,uint8_t * key,size_t len)255 bool SrtpFilter::ParseKeyParams(const std::string& key_params,
256 uint8_t* key,
257 size_t len) {
258 // example key_params: "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2"
259
260 // Fail if key-method is wrong.
261 if (!absl::StartsWith(key_params, "inline:")) {
262 return false;
263 }
264
265 // Fail if base64 decode fails, or the key is the wrong size.
266 std::string key_b64(key_params.substr(7)), key_str;
267 if (!rtc::Base64::Decode(key_b64, rtc::Base64::DO_STRICT, &key_str,
268 nullptr) ||
269 key_str.size() != len) {
270 return false;
271 }
272
273 memcpy(key, key_str.c_str(), len);
274 // TODO(bugs.webrtc.org/8905): Switch to ZeroOnFreeBuffer for storing
275 // sensitive data.
276 rtc::ExplicitZeroMemory(&key_str[0], key_str.size());
277 return true;
278 }
279
280 } // namespace cricket
281