• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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